Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > linux.kernel > #1417111 > unrolled thread
| Started by | Roger Quadros <rogerq@ti.com> |
|---|---|
| First post | 2016-06-08 11:10 +0200 |
| Last post | 2016-06-09 12:20 +0200 |
| Articles | 20 on this page of 33 — 6 participants |
Back to article view | Back to linux.kernel
[PATCH v9 00/14] USB OTG/dual-role framework Roger Quadros <rogerq@ti.com> - 2016-06-08 11:10 +0200
[PATCH v9 10/14] usb: otg: add hcd companion support Roger Quadros <rogerq@ti.com> - 2016-06-08 11:10 +0200
[PATCH v9 11/14] usb: otg: use dev_dbg() instead of VDBG() Roger Quadros <rogerq@ti.com> - 2016-06-08 11:10 +0200
Re: [PATCH v9 11/14] usb: otg: use dev_dbg() instead of VDBG() Joe Perches <joe@perches.com> - 2016-06-08 17:20 +0200
Re: [PATCH v9 11/14] usb: otg: use dev_dbg() instead of VDBG() Roger Quadros <rogerq@ti.com> - 2016-06-09 09:30 +0200
[PATCH v10 11/14] usb: otg: use dev_dbg() instead of VDBG() Roger Quadros <rogerq@ti.com> - 2016-06-09 10:00 +0200
Re: [PATCH v10 11/14] usb: otg: use dev_dbg() instead of VDBG() Joe Perches <joe@perches.com> - 2016-06-09 17:50 +0200
Re: [PATCH v10 11/14] usb: otg: use dev_dbg() instead of VDBG() Roger Quadros <rogerq@ti.com> - 2016-06-10 09:10 +0200
[PATCH v9 02/14] usb: otg-fsm: Prevent build warning "VDBG" redefined Roger Quadros <rogerq@ti.com> - 2016-06-08 11:10 +0200
[PATCH v9 09/14] usb: of: add an API to get OTG device from USB controller node Roger Quadros <rogerq@ti.com> - 2016-06-08 11:10 +0200
[PATCH v9 14/14] usb: host: xhci-plat: Add otg device to platform data Roger Quadros <rogerq@ti.com> - 2016-06-08 11:10 +0200
[PATCH v9 08/14] usb: otg: add OTG/dual-role core Roger Quadros <rogerq@ti.com> - 2016-06-08 11:10 +0200
Re: [PATCH v9 08/14] usb: otg: add OTG/dual-role core Peter Chen <hzpeterchen@gmail.com> - 2016-06-08 12:00 +0200
Re: [PATCH v9 08/14] usb: otg: add OTG/dual-role core Roger Quadros <rogerq@ti.com> - 2016-06-08 12:20 +0200
Re: [PATCH v9 08/14] usb: otg: add OTG/dual-role core Peter Chen <hzpeterchen@gmail.com> - 2016-06-08 14:50 +0200
Re: [PATCH v9 08/14] usb: otg: add OTG/dual-role core Roger Quadros <rogerq@ti.com> - 2016-06-09 09:30 +0200
[PATCH v10 08/14] usb: otg: add OTG/dual-role core Roger Quadros <rogerq@ti.com> - 2016-06-09 10:00 +0200
Re: [PATCH v10 08/14] usb: otg: add OTG/dual-role core Sergei Shtylyov <sergei.shtylyov@cogentembedded.com> - 2016-06-09 14:40 +0200
Re: [PATCH v10 08/14] usb: otg: add OTG/dual-role core Roger Quadros <rogerq@ti.com> - 2016-06-10 09:10 +0200
Re: [PATCH v10 08/14] usb: otg: add OTG/dual-role core Roger Quadros <rogerq@ti.com> - 2016-06-10 12:20 +0200
Re: [PATCH v10 08/14] usb: otg: add OTG/dual-role core Sergei Shtylyov <sergei.shtylyov@cogentembedded.com> - 2016-06-10 12:50 +0200
Re: [PATCH v10 08/14] usb: otg: add OTG/dual-role core Roger Quadros <rogerq@ti.com> - 2016-06-10 13:00 +0200
Re: [PATCH v10 08/14] usb: otg: add OTG/dual-role core Felipe Balbi <balbi@kernel.org> - 2016-06-10 13:10 +0200
[PATCH v9 13/14] usb: gadget: udc: adapt to OTG core Roger Quadros <rogerq@ti.com> - 2016-06-08 11:10 +0200
RE: [PATCH v9 13/14] usb: gadget: udc: adapt to OTG core Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com> - 2016-06-09 12:50 +0200
Re: [PATCH v9 13/14] usb: gadget: udc: adapt to OTG core Roger Quadros <rogerq@ti.com> - 2016-06-10 09:10 +0200
[PATCH v9 07/14] usb: otg: get rid of CONFIG_USB_OTG_FSM in favour of CONFIG_USB_OTG Roger Quadros <rogerq@ti.com> - 2016-06-08 11:20 +0200
[PATCH v9 01/14] usb: hcd: Initialize hcd->flags to 0 Roger Quadros <rogerq@ti.com> - 2016-06-08 11:20 +0200
[PATCH v9 05/14] usb: otg-fsm: move host controller operations into usb_otg->hcd_ops Roger Quadros <rogerq@ti.com> - 2016-06-08 11:20 +0200
[PATCH v9 06/14] usb: gadget.h: Add OTG to gadget interface Roger Quadros <rogerq@ti.com> - 2016-06-08 11:20 +0200
Re: [PATCH v9 00/14] USB OTG/dual-role framework Peter Chen <hzpeterchen@gmail.com> - 2016-06-09 07:30 +0200
Re: [PATCH v9 00/14] USB OTG/dual-role framework Roger Quadros <rogerq@ti.com> - 2016-06-09 09:40 +0200
RE: [PATCH v9 00/14] USB OTG/dual-role framework Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com> - 2016-06-09 12:20 +0200
Page 1 of 2 [1] 2 Next page →
| From | Roger Quadros <rogerq@ti.com> |
|---|---|
| Date | 2016-06-08 11:10 +0200 |
| Subject | [PATCH v9 00/14] USB OTG/dual-role framework |
| Message-ID | <rHHER-6Fd-3@gated-at.bofh.it> |
Hi,
This series centralizes OTG/Dual-role functionality in the kernel.
As of now I've got Dual-role functionality working pretty reliably on
dra7-evm and am437x-gp-evm.
DWC3 controller and TI platform related patches will be sent separately.
Series is based on v4.7-rc1.
Why?:
----
Currently there is no central location where OTG/dual-role functionality is
implemented in the Linux USB stack and every USB controller driver is
doing their own thing for OTG/dual-role. We can benefit from code-reuse
and simplicity by adding the OTG/dual-role core driver.
Newer OTG cores support standard host interface (e.g. xHCI) so
host and gadget functionality are no longer closely knit like older
cores. There needs to be a way to co-ordinate the operation of the
host and gadget controllers in dual-role mode. i.e. to stop and start them
from a central location. This central location should be the
USB OTG/dual-role core.
Host and gadget controllers might be sharing resources and can't
be always running. One has to be stopped for the other to run.
This couldn't be done till now but can be done from the OTG core.
What?:
-----
The OTG/dual-role core consists of a set of APIs that allow
registration of OTG controller device and OTG capable host and gadget
controllers.
- The OTG controller driver can provide the OTG capabilities and the
Finite State Machine work function via 'struct usb_otg_config'
at the time of registration i.e. usb_otg_register();
struct usb_otg *usb_otg_register(struct device *dev,
struct usb_otg_config *config);
int usb_otg_unregister(struct device *dev);
/**
* struct usb_otg_config - otg controller configuration
* @caps: otg capabilities of the controller
* @ops: otg fsm operations
* @otg_work: optional custom otg state machine work function
*/
struct usb_otg_config {
struct usb_otg_caps *otg_caps;
struct otg_fsm_ops *fsm_ops;
void (*otg_work)(struct work_struct *work);
};
The dual-role state machine is built-into the OTG core so nothing
special needs to be provided if only dual-role functionality is desired.
The low level OTG controller driver ops are povided via
'struct otg_fsm_ops *fsm_ops' in the 'struct usb_otg_config'.
After registration, the OTG core waits for host, gadget controller
and the gadget function driver to be registered. Once all resources are
available it instantiates the Finite State Machine (FSM).
The host/gadget controllers are started/stopped according to the FSM.
- Host and gadget controllers that are a part of OTG/dual-role port must
use the OTG core provided APIs to add/remove the host/gadget.
i.e. hosts must use usb_otg_add_hcd() usb_otg_remove_hcd(),,
gadgets must use usb_otg_add_gadget_udc() usb_del_gadget_udc().
This ensures that the host and gadget controllers are not started till
the state machine is ready and the right bus conditions are met.
It also allows the host and gadget controllers to provide the OTG
controller device to link them together. For Device tree boots
the related OTG controller is automatically picked up via the
'otg-controller' property in the Host/Gadget controller nodes.
int usb_otg_add_hcd(struct usb_hcd *hcd,
unsigned int irqnum, unsigned long irqflags,
struct device *otg_dev);
void usb_otg_remove_hcd(struct usb_hcd *hcd);
int usb_otg_add_gadget_udc(struct device *parent,
struct usb_gadget *gadget,
struct device *otg_dev);
usb_del_gadget_udc() must be used for removal.
- During the lifetime of the FSM, the OTG controller driver can provide
inputs event changes using usb_otg_sync_inputs(). The OTG core will
then schedule the FSM work function (or internal dual-role state machine)
to update the FSM state. The FSM then calls the OTG controller
operations (fsm_ops) as necessary.
void usb_otg_sync_inputs(struct usb_otg *otg);
- The following 2 functions are provided as helpers for use by the
OTG controller driver to start/stop the host/gadget controllers.
int usb_otg_start_host(struct usb_otg *otg, int on);
int usb_otg_start_gadget(struct usb_otg *otg, int on);
- The following function is provided for use by the USB host stack
to sync OTG related events to the OTG state machine.
e.g. change in host_bus->b_hnp_enable, gadget->b_hnp_enable
int usb_otg_kick_fsm(struct device *otg_device);
- The following function is provided for use by the USB gadget stack
to notify OTG/DRD about gadget function driver being ready/not-ready
int usb_otg_gadget_ready(struct usb_gadget *gadget, bool ready)
Changelog:
---------
v9:
- In the DT bindings, clearly indicate which properties are for OTG controller
and which ones are for host/device controllers
- Removed host/gadget wait list from otg core. Use defer probing if host/gadget
registers before dual-role/otg controller.
- move gadget registering to otg core from udc_bind_to_driver()/usb_gadget_remove_driver()
to usb_add_gadget_udc_release()/usb_del_gadget_udc(). This means that we need
an additional mechanism to know when the gadget function driver registers.
So we add usb_otg_gadget_ready() API to allow gadget to notify otg core about
ready/not-ready function driver.
- update otg->caps based on capabilities provided by the controller and the
device tree overrides.
- in usb_drd_work() we must keep calling the drd_statemachine() as long as
there is a state change.
- updated kconfig so that usbotg can be a module when both host and gadget
are modules.
- rebased on v4.7-rc1
v8:
- split out start/stop gadget and connect/disconnect operations.
- make CONFIG_OTG dpend on CONFIG_USB_GADGET as well apart from CONFIG_USB
- use create_freezable_workqueue() for OTG work as per Peter's suggestion.
- remove usb-otg.h as we're not initializing any OTG timers.
- don't include unnecessary headers in usb-otg.c (i.e. hrtimer.h & ktime.h)
v7:
- added dual-role support for host controllers requiring a companion
controller. e.g. EHCI + OHCI.
- added of_usb_get_otg() to get the OTG controller device
from the USB controller's device node.
- addressed review comments.
v6:
- added otg specific APIs for host/gadget registration. behaviour of
original host/gadget API remains unchanged. Platform devices can now
pass the otg device explicitly while registering host/gadget.
- moved hcd specific operations from struct otg_fsm to struct hcd_ops.
- made struct usb_otg mandatory for all otg related APIs.
- allow otg controller to provide it's own otg_work function so that
it can implement it's own state machine.
- removed otg fsm and timers from usb-otg.c. Only dual-role state machine
is implemented.
- vbus is controlled in the dual-role state machine.
- PM runtime is used around drd_statemachine().
- added otg_dev to xhci platform data to allow platform code to specify
the otg controller tied to the xhci host controller.
v5: Internal version. Not sent to mailing list
v4:
- Added DT support for tying otg-controller to host and gadget
controllers. For DT we no longer have the constraint that
OTG controller needs to be parent of host and gadget. They can be
tied together using the "otg-controller" property.
- Relax the requirement for DT case that otg controller must register
before host/gadget. We maintain a wait list of host/gadget devices
waiting on the otg controller.
- Use a single struct usb_otg for otg data.
- Don't override host/gadget start/stop APIs. Let the controller
drivers do what they want as they know best. Helper API is provided
for controller start/stop that controller driver can use.
- Introduce struct usb_otg_config to pass the otg capabilities,
otg ops and otg timer timeouts during otg controller registration.
- rebased on Greg's usb.git/usb-next
v3:
- all otg related definations now in otg.h
- single kernel config USB_OTG to enable OTG core and FSM.
- resolved symbol dependency issues.
- use dev_vdbg instead of VDBG() in usb-otg-fsm.c
- rebased on v4.2-rc1
v2:
- Use add/remove_hcd() instead of start/stop_hcd() to enable/disable
the host controller
- added dual-role-device (DRD) state machine which is a much simpler
mode of operation when compared to OTG. Here we don't support fancy
OTG features like HNP, SRP, on the fly role-swap. The mode of operation
is determined based on ID pin (cable type) and the role doesn't change
till the cable type changes.
--
cheers,
-roger
Roger Quadros (13):
usb: hcd: Initialize hcd->flags to 0
usb: otg-fsm: Prevent build warning "VDBG" redefined
usb: hcd.h: Add OTG to HCD interface
usb: otg-fsm: use usb_otg wherever possible
usb: otg-fsm: move host controller operations into usb_otg->hcd_ops
usb: gadget.h: Add OTG to gadget interface
usb: otg: get rid of CONFIG_USB_OTG_FSM in favour of CONFIG_USB_OTG
usb: otg: add OTG/dual-role core
usb: of: add an API to get OTG device from USB controller node
usb: otg: use dev_dbg() instead of VDBG()
usb: hcd: Adapt to OTG core
usb: gadget: udc: adapt to OTG core
usb: host: xhci-plat: Add otg device to platform data
Yoshihiro Shimoda (1):
usb: otg: add hcd companion support
Documentation/devicetree/bindings/usb/generic.txt | 6 +
Documentation/usb/chipidea.txt | 2 +-
drivers/usb/Kconfig | 18 +
drivers/usb/Makefile | 1 +
drivers/usb/chipidea/Makefile | 2 +-
drivers/usb/chipidea/ci.h | 3 +-
drivers/usb/chipidea/core.c | 14 +-
drivers/usb/chipidea/debug.c | 2 +-
drivers/usb/chipidea/otg_fsm.c | 176 ++---
drivers/usb/chipidea/otg_fsm.h | 2 +-
drivers/usb/chipidea/udc.c | 17 +-
drivers/usb/common/Makefile | 5 +-
drivers/usb/common/common.c | 27 +
drivers/usb/common/usb-otg-fsm.c | 203 +++---
drivers/usb/common/usb-otg.c | 846 ++++++++++++++++++++++
drivers/usb/core/Kconfig | 22 -
drivers/usb/core/hcd.c | 56 ++
drivers/usb/gadget/Kconfig | 1 +
drivers/usb/gadget/udc/udc-core.c | 202 +++++-
drivers/usb/host/xhci-plat.c | 35 +-
drivers/usb/phy/Kconfig | 2 +-
drivers/usb/phy/phy-fsl-usb.c | 155 ++--
drivers/usb/phy/phy-fsl-usb.h | 3 +-
include/linux/usb/gadget.h | 22 +
include/linux/usb/hcd.h | 29 +
include/linux/usb/of.h | 9 +
include/linux/usb/otg-fsm.h | 154 +---
include/linux/usb/otg.h | 293 +++++++-
include/linux/usb/xhci_pdriver.h | 3 +
29 files changed, 1848 insertions(+), 462 deletions(-)
create mode 100644 drivers/usb/common/usb-otg.c
--
2.7.4
[toc] | [next] | [standalone]
| From | Roger Quadros <rogerq@ti.com> |
|---|---|
| Date | 2016-06-08 11:10 +0200 |
| Subject | [PATCH v9 10/14] usb: otg: add hcd companion support |
| Message-ID | <rHHER-6Fd-11@gated-at.bofh.it> |
| In reply to | #1417111 |
From: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
Since some host controller (e.g. EHCI) needs a companion host controller
(e.g. OHCI), this patch adds such a configuration to use it in the OTG
core.
Signed-off-by: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
Signed-off-by: Roger Quadros <rogerq@ti.com>
Acked-by: Peter Chen <peter.chen@nxp.com>
Acked-by: Rob Herring <robh@kernel.org>
---
Documentation/devicetree/bindings/usb/generic.txt | 3 +++
drivers/usb/common/usb-otg.c | 32 ++++++++++++++++-------
include/linux/usb/otg.h | 7 ++++-
3 files changed, 32 insertions(+), 10 deletions(-)
diff --git a/Documentation/devicetree/bindings/usb/generic.txt b/Documentation/devicetree/bindings/usb/generic.txt
index f6866c1..1db1c33 100644
--- a/Documentation/devicetree/bindings/usb/generic.txt
+++ b/Documentation/devicetree/bindings/usb/generic.txt
@@ -27,6 +27,9 @@ Optional properties:
- otg-controller: phandle to otg controller. Host or gadget controllers can
contain this property to link it to a particular OTG
controller.
+ - hcd-needs-companion: must be present if otg controller is dealing with
+ EHCI host controller that needs a companion OHCI host
+ controller.
This is an attribute to a USB controller such as:
diff --git a/drivers/usb/common/usb-otg.c b/drivers/usb/common/usb-otg.c
index b5bdf62..98fcfa2 100644
--- a/drivers/usb/common/usb-otg.c
+++ b/drivers/usb/common/usb-otg.c
@@ -19,6 +19,7 @@
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_platform.h>
+#include <linux/usb/of.h>
#include <linux/usb/otg.h>
#include <linux/usb/gadget.h>
#include <linux/workqueue.h>
@@ -348,6 +349,10 @@ struct usb_otg *usb_otg_register(struct device *dev,
else
INIT_WORK(&otg->work, usb_drd_work);
+ if (of_find_property(dev->of_node, "hcd-needs-companion", NULL) ||
+ config->hcd_needs_companion) /* needs companion ? */
+ otg->flags |= OTG_FLAG_HCD_NEEDS_COMPANION;
+
otg->wq = create_freezable_workqueue("usb_otg");
if (!otg->wq) {
dev_err(dev, "otg: %s: can't create workqueue\n",
@@ -562,15 +567,18 @@ int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
/* HCD will be started by OTG fsm when needed */
mutex_lock(&otg->fsm.lock);
if (otg->primary_hcd.hcd) {
- /* probably a shared HCD ? */
- if (usb_otg_hcd_is_primary_hcd(hcd)) {
+ /* probably a shared HCD or a companion OHCI HCD ? */
+ if (!(otg->flags & OTG_FLAG_HCD_NEEDS_COMPANION) &&
+ usb_otg_hcd_is_primary_hcd(hcd)) {
dev_err(otg_dev, "otg: primary host already registered\n");
goto err;
}
- if (hcd->shared_hcd == otg->primary_hcd.hcd) {
+ if (otg->flags & OTG_FLAG_HCD_NEEDS_COMPANION ||
+ (hcd->shared_hcd == otg->primary_hcd.hcd)) {
if (otg->shared_hcd.hcd) {
- dev_err(otg_dev, "otg: shared host already registered\n");
+ dev_err(otg_dev,
+ "otg: shared/companion host already registered\n");
goto err;
}
@@ -578,10 +586,12 @@ int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
otg->shared_hcd.irqnum = irqnum;
otg->shared_hcd.irqflags = irqflags;
otg->shared_hcd.ops = ops;
- dev_info(otg_dev, "otg: shared host %s registered\n",
+ dev_info(otg_dev,
+ "otg: shared/companion host %s registered\n",
dev_name(hcd->self.controller));
} else {
- dev_err(otg_dev, "otg: invalid shared host %s\n",
+ dev_err(otg_dev,
+ "otg: invalid shared/companion host %s\n",
dev_name(hcd->self.controller));
goto err;
}
@@ -604,14 +614,17 @@ int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
* we're ready only if we have shared HCD
* or we don't need shared HCD.
*/
- if (otg->shared_hcd.hcd || !otg->primary_hcd.hcd->shared_hcd) {
+ if (otg->shared_hcd.hcd ||
+ (!(otg->flags & OTG_FLAG_HCD_NEEDS_COMPANION) &&
+ !otg->primary_hcd.hcd->shared_hcd)) {
otg->host = hcd_to_bus(hcd);
/* FIXME: set bus->otg_port if this is true OTG port with HNP */
/* start FSM */
usb_otg_start_fsm(otg);
} else {
- dev_dbg(otg_dev, "otg: can't start till shared host registers\n");
+ dev_dbg(otg_dev,
+ "otg: can't start till shared/companion host registers\n");
}
mutex_unlock(&otg->fsm.lock);
@@ -659,7 +672,8 @@ int usb_otg_unregister_hcd(struct usb_hcd *hcd)
dev_name(hcd_dev));
} else if (hcd == otg->shared_hcd.hcd) {
otg->shared_hcd.hcd = NULL;
- dev_info(otg_dev, "otg: shared host %s unregistered\n",
+ dev_info(otg_dev,
+ "otg: shared/companion host %s unregistered\n",
dev_name(hcd_dev));
} else {
mutex_unlock(&otg->fsm.lock);
diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h
index cc58a2d..4c585ec 100644
--- a/include/linux/usb/otg.h
+++ b/include/linux/usb/otg.h
@@ -72,7 +72,8 @@ struct usb_otg_caps {
* @list: list of otg controllers
* @work: otg state machine work
* @wq: otg state machine work queue
- * @flags: to track if host/gadget is running
+ * @flags: to track if host/gadget is running, or to indicate if hcd needs
+ * companion
*/
struct usb_otg {
u8 default_a;
@@ -100,6 +101,7 @@ struct usb_otg {
u32 flags;
#define OTG_FLAG_GADGET_RUNNING (1 << 0)
#define OTG_FLAG_HOST_RUNNING (1 << 1)
+#define OTG_FLAG_HCD_NEEDS_COMPANION (1 << 2)
/* use otg->fsm.lock for serializing access */
/*------------- deprecated interface -----------------------------*/
@@ -126,11 +128,14 @@ struct usb_otg {
* @caps: otg capabilities of the controller
* @ops: otg fsm operations
* @otg_work: optional custom otg state machine work function
+ * @hcd_needs_companion: Indicates if host controller needs a companion
+ * controller
*/
struct usb_otg_config {
struct usb_otg_caps *otg_caps;
struct otg_fsm_ops *fsm_ops;
void (*otg_work)(struct work_struct *work);
+ bool hcd_needs_companion;
};
extern const char *usb_otg_state_string(enum usb_otg_state state);
--
2.7.4
[toc] | [prev] | [next] | [standalone]
| From | Roger Quadros <rogerq@ti.com> |
|---|---|
| Date | 2016-06-08 11:10 +0200 |
| Subject | [PATCH v9 11/14] usb: otg: use dev_dbg() instead of VDBG() |
| Message-ID | <rHHES-6Fd-27@gated-at.bofh.it> |
| In reply to | #1417111 |
Now that we have a device reference in struct usb_otg
let's use dev_dbg() for debug messages.
Signed-off-by: Roger Quadros <rogerq@ti.com>
Acked-by: Peter Chen <peter.chen@nxp.com>
---
drivers/usb/common/usb-otg-fsm.c | 19 +++++++------------
1 file changed, 7 insertions(+), 12 deletions(-)
diff --git a/drivers/usb/common/usb-otg-fsm.c b/drivers/usb/common/usb-otg-fsm.c
index 482d4c9..e6e58c2 100644
--- a/drivers/usb/common/usb-otg-fsm.c
+++ b/drivers/usb/common/usb-otg-fsm.c
@@ -30,13 +30,6 @@
#include <linux/usb/otg.h>
#include <linux/usb/otg-fsm.h>
-#ifdef VERBOSE
-#define VDBG(fmt, args...) pr_debug("[%s] " fmt, \
- __func__, ## args)
-#else
-#define VDBG(stuff...) do {} while (0)
-#endif
-
/* Change USB protocol when there is a protocol change */
static int otg_set_protocol(struct otg_fsm *fsm, int protocol)
{
@@ -44,8 +37,9 @@ static int otg_set_protocol(struct otg_fsm *fsm, int protocol)
int ret = 0;
if (fsm->protocol != protocol) {
- VDBG("Changing role fsm->protocol= %d; new protocol= %d\n",
- fsm->protocol, protocol);
+ dev_vdbg(otg->dev,
+ "Changing role fsm->protocol= %d; new protocol= %d\n",
+ fsm->protocol, protocol);
/* stop old protocol */
if (fsm->protocol == PROTO_HOST)
ret = otg_start_host(otg, 0);
@@ -226,7 +220,7 @@ static int otg_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state)
if (otg->state == new_state)
return 0;
- VDBG("Set state: %s\n", usb_otg_state_string(new_state));
+ dev_vdbg(otg->dev, "Set state: %s\n", usb_otg_state_string(new_state));
otg_leave_state(fsm, otg->state);
switch (new_state) {
case OTG_STATE_B_IDLE:
@@ -358,7 +352,7 @@ int otg_statemachine(struct usb_otg *otg)
switch (state) {
case OTG_STATE_UNDEFINED:
- VDBG("fsm->id = %d\n", fsm->id);
+ dev_vdbg(otg->dev, "fsm->id = %d\n", fsm->id);
if (fsm->id)
otg_set_state(fsm, OTG_STATE_B_IDLE);
else
@@ -466,7 +460,8 @@ int otg_statemachine(struct usb_otg *otg)
}
mutex_unlock(&fsm->lock);
- VDBG("quit statemachine, changed = %d\n", fsm->state_changed);
+ dev_vdbg(otg->dev, "quit statemachine, changed = %d\n",
+ fsm->state_changed);
return fsm->state_changed;
}
EXPORT_SYMBOL_GPL(otg_statemachine);
--
2.7.4
[toc] | [prev] | [next] | [standalone]
| From | Joe Perches <joe@perches.com> |
|---|---|
| Date | 2016-06-08 17:20 +0200 |
| Subject | Re: [PATCH v9 11/14] usb: otg: use dev_dbg() instead of VDBG() |
| Message-ID | <rHNqW-1PK-27@gated-at.bofh.it> |
| In reply to | #1417116 |
On Wed, 2016-06-08 at 12:03 +0300, Roger Quadros wrote:
> Now that we have a device reference in struct usb_otg
> let's use dev_dbg() for debug messages.
dev_vdbg vs dev_dbg
The patch subject and commit message don't match the code changes.
> diff --git a/drivers/usb/common/usb-otg-fsm.c b/drivers/usb/common/usb-otg-fsm.c
[]
> @@ -44,8 +37,9 @@ static int otg_set_protocol(struct otg_fsm *fsm, int protocol)
> int ret = 0;
>
> if (fsm->protocol != protocol) {
> - VDBG("Changing role fsm->protocol= %d; new protocol= %d\n",
> - fsm->protocol, protocol);
> + dev_vdbg(otg->dev,
> + "Changing role fsm->protocol= %d; new protocol= %d\n",
> + fsm->protocol, protocol);
etc...
[toc] | [prev] | [next] | [standalone]
| From | Roger Quadros <rogerq@ti.com> |
|---|---|
| Date | 2016-06-09 09:30 +0200 |
| Subject | Re: [PATCH v9 11/14] usb: otg: use dev_dbg() instead of VDBG() |
| Message-ID | <rI2zE-3dZ-23@gated-at.bofh.it> |
| In reply to | #1417572 |
On 08/06/16 18:10, Joe Perches wrote:
> On Wed, 2016-06-08 at 12:03 +0300, Roger Quadros wrote:
>> Now that we have a device reference in struct usb_otg
>> let's use dev_dbg() for debug messages.
>
> dev_vdbg vs dev_dbg
>
> The patch subject and commit message don't match the code changes.
Indeed. Will fix it. Thanks.
>
>> diff --git a/drivers/usb/common/usb-otg-fsm.c b/drivers/usb/common/usb-otg-fsm.c
> []
>> @@ -44,8 +37,9 @@ static int otg_set_protocol(struct otg_fsm *fsm, int protocol)
>> int ret = 0;
>>
>> if (fsm->protocol != protocol) {
>> - VDBG("Changing role fsm->protocol= %d; new protocol= %d\n",
>> - fsm->protocol, protocol);
>> + dev_vdbg(otg->dev,
>> + "Changing role fsm->protocol= %d; new protocol= %d\n",
>> + fsm->protocol, protocol);
>
> etc...
>
--
cheers,
-roger
[toc] | [prev] | [next] | [standalone]
| From | Roger Quadros <rogerq@ti.com> |
|---|---|
| Date | 2016-06-09 10:00 +0200 |
| Subject | [PATCH v10 11/14] usb: otg: use dev_dbg() instead of VDBG() |
| Message-ID | <rI32G-3oT-13@gated-at.bofh.it> |
| In reply to | #1417116 |
Now that we have a device reference in struct usb_otg
let's use dev_vdbg() for debug messages instead of VDBG().
Signed-off-by: Roger Quadros <rogerq@ti.com>
Acked-by: Peter Chen <peter.chen@nxp.com>
---
v10:
- in the commit message say dev_vdbg() instead of dev_dbg()
drivers/usb/common/usb-otg-fsm.c | 19 +++++++------------
1 file changed, 7 insertions(+), 12 deletions(-)
diff --git a/drivers/usb/common/usb-otg-fsm.c b/drivers/usb/common/usb-otg-fsm.c
index 482d4c9..e6e58c2 100644
--- a/drivers/usb/common/usb-otg-fsm.c
+++ b/drivers/usb/common/usb-otg-fsm.c
@@ -30,13 +30,6 @@
#include <linux/usb/otg.h>
#include <linux/usb/otg-fsm.h>
-#ifdef VERBOSE
-#define VDBG(fmt, args...) pr_debug("[%s] " fmt, \
- __func__, ## args)
-#else
-#define VDBG(stuff...) do {} while (0)
-#endif
-
/* Change USB protocol when there is a protocol change */
static int otg_set_protocol(struct otg_fsm *fsm, int protocol)
{
@@ -44,8 +37,9 @@ static int otg_set_protocol(struct otg_fsm *fsm, int protocol)
int ret = 0;
if (fsm->protocol != protocol) {
- VDBG("Changing role fsm->protocol= %d; new protocol= %d\n",
- fsm->protocol, protocol);
+ dev_vdbg(otg->dev,
+ "Changing role fsm->protocol= %d; new protocol= %d\n",
+ fsm->protocol, protocol);
/* stop old protocol */
if (fsm->protocol == PROTO_HOST)
ret = otg_start_host(otg, 0);
@@ -226,7 +220,7 @@ static int otg_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state)
if (otg->state == new_state)
return 0;
- VDBG("Set state: %s\n", usb_otg_state_string(new_state));
+ dev_vdbg(otg->dev, "Set state: %s\n", usb_otg_state_string(new_state));
otg_leave_state(fsm, otg->state);
switch (new_state) {
case OTG_STATE_B_IDLE:
@@ -358,7 +352,7 @@ int otg_statemachine(struct usb_otg *otg)
switch (state) {
case OTG_STATE_UNDEFINED:
- VDBG("fsm->id = %d\n", fsm->id);
+ dev_vdbg(otg->dev, "fsm->id = %d\n", fsm->id);
if (fsm->id)
otg_set_state(fsm, OTG_STATE_B_IDLE);
else
@@ -466,7 +460,8 @@ int otg_statemachine(struct usb_otg *otg)
}
mutex_unlock(&fsm->lock);
- VDBG("quit statemachine, changed = %d\n", fsm->state_changed);
+ dev_vdbg(otg->dev, "quit statemachine, changed = %d\n",
+ fsm->state_changed);
return fsm->state_changed;
}
EXPORT_SYMBOL_GPL(otg_statemachine);
--
2.7.4
[toc] | [prev] | [next] | [standalone]
| From | Joe Perches <joe@perches.com> |
|---|---|
| Date | 2016-06-09 17:50 +0200 |
| Subject | Re: [PATCH v10 11/14] usb: otg: use dev_dbg() instead of VDBG() |
| Message-ID | <rIanw-8jM-7@gated-at.bofh.it> |
| In reply to | #1418147 |
On Thu, 2016-06-09 at 10:55 +0300, Roger Quadros wrote: > Now that we have a device reference in struct usb_otg > let's use dev_vdbg() for debug messages instead of VDBG(). > > Signed-off-by: Roger Quadros <rogerq@ti.com> > Acked-by: Peter Chen <peter.chen@nxp.com> > --- > v10: > - in the commit message say dev_vdbg() instead of dev_dbg() 50% - partial credit, see: patch/email subject.
[toc] | [prev] | [next] | [standalone]
| From | Roger Quadros <rogerq@ti.com> |
|---|---|
| Date | 2016-06-10 09:10 +0200 |
| Subject | Re: [PATCH v10 11/14] usb: otg: use dev_dbg() instead of VDBG() |
| Message-ID | <rIoJP-1vU-23@gated-at.bofh.it> |
| In reply to | #1418443 |
On 09/06/16 18:43, Joe Perches wrote: > On Thu, 2016-06-09 at 10:55 +0300, Roger Quadros wrote: >> Now that we have a device reference in struct usb_otg >> let's use dev_vdbg() for debug messages instead of VDBG(). >> >> Signed-off-by: Roger Quadros <rogerq@ti.com> >> Acked-by: Peter Chen <peter.chen@nxp.com> >> --- >> v10: >> - in the commit message say dev_vdbg() instead of dev_dbg() > > 50% - partial credit, see: patch/email subject. > Oops. Will fix. :) cheers, -roger
[toc] | [prev] | [next] | [standalone]
| From | Roger Quadros <rogerq@ti.com> |
|---|---|
| Date | 2016-06-08 11:10 +0200 |
| Subject | [PATCH v9 02/14] usb: otg-fsm: Prevent build warning "VDBG" redefined |
| Message-ID | <rHHES-6Fd-35@gated-at.bofh.it> |
| In reply to | #1417111 |
If usb/otg-fsm.h and usb/composite.h are included together
then it results in the build warning [1].
Prevent that by defining VDBG locally.
Also get rid of MPC_LOC which doesn't seem to be used
by anyone.
[1] - warning fixed by this patch:
In file included from drivers/usb/dwc3/core.h:33,
from drivers/usb/dwc3/ep0.c:33:
include/linux/usb/otg-fsm.h:30:1: warning: "VDBG" redefined
In file included from drivers/usb/dwc3/ep0.c:31:
include/linux/usb/composite.h:615:1: warning: this is the location
of the previous definition
Signed-off-by: Roger Quadros <rogerq@ti.com>
Acked-by: Peter Chen <peter.chen@nxp.com>
---
drivers/usb/common/usb-otg-fsm.c | 7 +++++++
drivers/usb/phy/phy-fsl-usb.c | 7 +++++++
include/linux/usb/otg-fsm.h | 15 ---------------
3 files changed, 14 insertions(+), 15 deletions(-)
diff --git a/drivers/usb/common/usb-otg-fsm.c b/drivers/usb/common/usb-otg-fsm.c
index 9059b7d..199dee0 100644
--- a/drivers/usb/common/usb-otg-fsm.c
+++ b/drivers/usb/common/usb-otg-fsm.c
@@ -30,6 +30,13 @@
#include <linux/usb/otg.h>
#include <linux/usb/otg-fsm.h>
+#ifdef VERBOSE
+#define VDBG(fmt, args...) pr_debug("[%s] " fmt, \
+ __func__, ## args)
+#else
+#define VDBG(stuff...) do {} while (0)
+#endif
+
/* Change USB protocol when there is a protocol change */
static int otg_set_protocol(struct otg_fsm *fsm, int protocol)
{
diff --git a/drivers/usb/phy/phy-fsl-usb.c b/drivers/usb/phy/phy-fsl-usb.c
index 94eb292..a8784ec 100644
--- a/drivers/usb/phy/phy-fsl-usb.c
+++ b/drivers/usb/phy/phy-fsl-usb.c
@@ -44,6 +44,13 @@
#include "phy-fsl-usb.h"
+#ifdef VERBOSE
+#define VDBG(fmt, args...) pr_debug("[%s] " fmt, \
+ __func__, ## args)
+#else
+#define VDBG(stuff...) do {} while (0)
+#endif
+
#define DRIVER_VERSION "Rev. 1.55"
#define DRIVER_AUTHOR "Jerry Huang/Li Yang"
#define DRIVER_DESC "Freescale USB OTG Transceiver Driver"
diff --git a/include/linux/usb/otg-fsm.h b/include/linux/usb/otg-fsm.h
index 7a03505..a0a8f87 100644
--- a/include/linux/usb/otg-fsm.h
+++ b/include/linux/usb/otg-fsm.h
@@ -21,21 +21,6 @@
#include <linux/mutex.h>
#include <linux/errno.h>
-#undef VERBOSE
-
-#ifdef VERBOSE
-#define VDBG(fmt, args...) pr_debug("[%s] " fmt , \
- __func__, ## args)
-#else
-#define VDBG(stuff...) do {} while (0)
-#endif
-
-#ifdef VERBOSE
-#define MPC_LOC printk("Current Location [%s]:[%d]\n", __FILE__, __LINE__)
-#else
-#define MPC_LOC do {} while (0)
-#endif
-
#define PROTO_UNDEF (0)
#define PROTO_HOST (1)
#define PROTO_GADGET (2)
--
2.7.4
[toc] | [prev] | [next] | [standalone]
| From | Roger Quadros <rogerq@ti.com> |
|---|---|
| Date | 2016-06-08 11:10 +0200 |
| Subject | [PATCH v9 09/14] usb: of: add an API to get OTG device from USB controller node |
| Message-ID | <rHHES-6Fd-29@gated-at.bofh.it> |
| In reply to | #1417111 |
The OTG controller and the USB controller can be linked via the
'otg-controller' property in the USB controller's device node.
of_usb_get_otg() can be used to get the OTG controller device
from the USB controller's device node.
Signed-off-by: Roger Quadros <rogerq@ti.com>
Acked-by: Peter Chen <peter.chen@nxp.com>
Acked-by: Rob Herring <robh@kernel.org>
---
Documentation/devicetree/bindings/usb/generic.txt | 3 +++
drivers/usb/common/common.c | 27 +++++++++++++++++++++++
include/linux/usb/of.h | 9 ++++++++
3 files changed, 39 insertions(+)
diff --git a/Documentation/devicetree/bindings/usb/generic.txt b/Documentation/devicetree/bindings/usb/generic.txt
index bba8257..f6866c1 100644
--- a/Documentation/devicetree/bindings/usb/generic.txt
+++ b/Documentation/devicetree/bindings/usb/generic.txt
@@ -24,6 +24,9 @@ Optional properties:
optional for OTG device.
- adp-disable: tells OTG controllers we want to disable OTG ADP, ADP is
optional for OTG device.
+ - otg-controller: phandle to otg controller. Host or gadget controllers can
+ contain this property to link it to a particular OTG
+ controller.
This is an attribute to a USB controller such as:
diff --git a/drivers/usb/common/common.c b/drivers/usb/common/common.c
index e3d0161..d7ec471 100644
--- a/drivers/usb/common/common.c
+++ b/drivers/usb/common/common.c
@@ -238,6 +238,33 @@ int of_usb_update_otg_caps(struct device_node *np,
}
EXPORT_SYMBOL_GPL(of_usb_update_otg_caps);
+#ifdef CONFIG_USB_OTG
+/**
+ * of_usb_get_otg - get the OTG controller linked to the USB controller
+ * @np: Pointer to the device_node of the USB controller
+ * @otg_caps: Pointer to the target usb_otg_caps to be set
+ *
+ * Returns the OTG controller device or NULL on error.
+ */
+struct device *of_usb_get_otg(struct device_node *np)
+{
+ struct device_node *otg_np;
+ struct platform_device *pdev;
+
+ otg_np = of_parse_phandle(np, "otg-controller", 0);
+ if (!otg_np)
+ return NULL;
+
+ pdev = of_find_device_by_node(otg_np);
+ of_node_put(otg_np);
+ if (!pdev)
+ return NULL;
+
+ return &pdev->dev;
+}
+EXPORT_SYMBOL_GPL(of_usb_get_otg);
+#endif
+
#endif
MODULE_LICENSE("GPL");
diff --git a/include/linux/usb/of.h b/include/linux/usb/of.h
index de3237f..499a4e8 100644
--- a/include/linux/usb/of.h
+++ b/include/linux/usb/of.h
@@ -40,6 +40,15 @@ static inline struct device_node *usb_of_get_child_node
}
#endif
+#if IS_ENABLED(CONFIG_OF) && IS_ENABLED(CONFIG_USB_OTG)
+struct device *of_usb_get_otg(struct device_node *np);
+#else
+static inline struct device *of_usb_get_otg(struct device_node *np)
+{
+ return NULL;
+}
+#endif
+
#if IS_ENABLED(CONFIG_OF) && IS_ENABLED(CONFIG_USB_SUPPORT)
enum usb_phy_interface of_usb_get_phy_mode(struct device_node *np);
#else
--
2.7.4
[toc] | [prev] | [next] | [standalone]
| From | Roger Quadros <rogerq@ti.com> |
|---|---|
| Date | 2016-06-08 11:10 +0200 |
| Subject | [PATCH v9 14/14] usb: host: xhci-plat: Add otg device to platform data |
| Message-ID | <rHHES-6Fd-33@gated-at.bofh.it> |
| In reply to | #1417111 |
Host controllers that are part of an OTG/dual-role instance
need to somehow pass the OTG controller device information
to the HCD core.
We use platform data to pass the OTG controller device.
Signed-off-by: Roger Quadros <rogerq@ti.com>
Reviewed-by: Peter Chen <peter.chen@nxp.com>
---
drivers/usb/host/xhci-plat.c | 35 ++++++++++++++++++++++++++++-------
include/linux/usb/xhci_pdriver.h | 3 +++
2 files changed, 31 insertions(+), 7 deletions(-)
diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c
index 676ea45..24d030a 100644
--- a/drivers/usb/host/xhci-plat.c
+++ b/drivers/usb/host/xhci-plat.c
@@ -239,11 +239,20 @@ static int xhci_plat_probe(struct platform_device *pdev)
goto put_usb3_hcd;
}
- ret = usb_add_hcd(hcd, irq, IRQF_SHARED);
+ if (pdata && pdata->otg_dev)
+ ret = usb_otg_add_hcd(hcd, irq, IRQF_SHARED, pdata->otg_dev);
+ else
+ ret = usb_add_hcd(hcd, irq, IRQF_SHARED);
+
if (ret)
goto disable_usb_phy;
- ret = usb_add_hcd(xhci->shared_hcd, irq, IRQF_SHARED);
+ if (pdata && pdata->otg_dev)
+ ret = usb_otg_add_hcd(xhci->shared_hcd, irq, IRQF_SHARED,
+ pdata->otg_dev);
+ else
+ ret = usb_add_hcd(xhci->shared_hcd, irq, IRQF_SHARED);
+
if (ret)
goto dealloc_usb2_hcd;
@@ -251,7 +260,10 @@ static int xhci_plat_probe(struct platform_device *pdev)
dealloc_usb2_hcd:
- usb_remove_hcd(hcd);
+ if (pdata && pdata->otg_dev)
+ usb_otg_remove_hcd(hcd);
+ else
+ usb_remove_hcd(hcd);
disable_usb_phy:
usb_phy_shutdown(hcd->usb_phy);
@@ -269,16 +281,25 @@ put_hcd:
return ret;
}
-static int xhci_plat_remove(struct platform_device *dev)
+static int xhci_plat_remove(struct platform_device *pdev)
{
- struct usb_hcd *hcd = platform_get_drvdata(dev);
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
struct clk *clk = xhci->clk;
+ struct usb_xhci_pdata *pdata = dev_get_platdata(&pdev->dev);
+
+ if (pdata && pdata->otg_dev)
+ usb_otg_remove_hcd(xhci->shared_hcd);
+ else
+ usb_remove_hcd(xhci->shared_hcd);
- usb_remove_hcd(xhci->shared_hcd);
usb_phy_shutdown(hcd->usb_phy);
- usb_remove_hcd(hcd);
+ if (pdata && pdata->otg_dev)
+ usb_otg_remove_hcd(hcd);
+ else
+ usb_remove_hcd(hcd);
+
usb_put_hcd(xhci->shared_hcd);
if (!IS_ERR(clk))
diff --git a/include/linux/usb/xhci_pdriver.h b/include/linux/usb/xhci_pdriver.h
index 376654b..5c68b83 100644
--- a/include/linux/usb/xhci_pdriver.h
+++ b/include/linux/usb/xhci_pdriver.h
@@ -18,10 +18,13 @@
*
* @usb3_lpm_capable: determines if this xhci platform supports USB3
* LPM capability
+ * @otg_dev: OTG controller device. Only requied if part of
+ * OTG/dual-role.
*
*/
struct usb_xhci_pdata {
unsigned usb3_lpm_capable:1;
+ struct device *otg_dev;
};
#endif /* __USB_CORE_XHCI_PDRIVER_H */
--
2.7.4
[toc] | [prev] | [next] | [standalone]
| From | Roger Quadros <rogerq@ti.com> |
|---|---|
| Date | 2016-06-08 11:10 +0200 |
| Subject | [PATCH v9 08/14] usb: otg: add OTG/dual-role core |
| Message-ID | <rHHER-6Fd-19@gated-at.bofh.it> |
| In reply to | #1417111 |
It provides APIs for the following tasks
- Registering an OTG/dual-role capable controller
- Registering Host and Gadget controllers to OTG core
- Providing inputs to and kicking the OTG state machine
Provide a dual-role device (DRD) state machine.
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 'b_sess_vld'.
Signed-off-by: Roger Quadros <rogerq@ti.com>
---
drivers/usb/Kconfig | 18 +
drivers/usb/Makefile | 1 +
drivers/usb/common/Makefile | 6 +-
drivers/usb/common/usb-otg.c | 832 +++++++++++++++++++++++++++++++++++++++++++
drivers/usb/core/Kconfig | 14 -
drivers/usb/gadget/Kconfig | 1 +
include/linux/usb/gadget.h | 2 +
include/linux/usb/hcd.h | 1 +
include/linux/usb/otg-fsm.h | 7 +
include/linux/usb/otg.h | 183 +++++++++-
10 files changed, 1035 insertions(+), 30 deletions(-)
create mode 100644 drivers/usb/common/usb-otg.c
diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index 8689dcb..ed596ec 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -32,6 +32,23 @@ if USB_SUPPORT
config USB_COMMON
tristate
+config USB_OTG_CORE
+ tristate
+
+config USB_OTG
+ bool "OTG/Dual-role support"
+ depends on PM && USB && USB_GADGET
+ default n
+ ---help---
+ The most notable feature of USB OTG is support for a
+ "Dual-Role" device, which can act as either a device
+ or a host. The initial role is decided by the type of
+ plug inserted and can be changed later when two dual
+ role devices talk to each other.
+
+ Select this only if your board has Mini-AB/Micro-AB
+ connector.
+
config USB_ARCH_HAS_HCD
def_bool y
@@ -40,6 +57,7 @@ config USB
tristate "Support for Host-side USB"
depends on USB_ARCH_HAS_HCD
select USB_COMMON
+ select USB_OTG_CORE
select NLS # for UTF-8 strings
---help---
Universal Serial Bus (USB) is a specification for a serial bus
diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
index dca7856..03f7204 100644
--- a/drivers/usb/Makefile
+++ b/drivers/usb/Makefile
@@ -59,5 +59,6 @@ obj-$(CONFIG_USB_RENESAS_USBHS) += renesas_usbhs/
obj-$(CONFIG_USB_GADGET) += gadget/
obj-$(CONFIG_USB_COMMON) += common/
+obj-$(CONFIG_USB_OTG_CORE) += common/
obj-$(CONFIG_USBIP_CORE) += usbip/
diff --git a/drivers/usb/common/Makefile b/drivers/usb/common/Makefile
index f8f2c88..5122b3f 100644
--- a/drivers/usb/common/Makefile
+++ b/drivers/usb/common/Makefile
@@ -7,5 +7,7 @@ usb-common-y += common.o
usb-common-$(CONFIG_USB_LED_TRIG) += led.o
obj-$(CONFIG_USB_ULPI_BUS) += ulpi.o
-usbotg-y := usb-otg-fsm.o
-obj-$(CONFIG_USB_OTG) += usbotg.o
+ifeq ($(CONFIG_USB_OTG),y)
+usbotg-y := usb-otg.o usb-otg-fsm.o
+obj-$(CONFIG_USB_OTG_CORE) += usbotg.o
+endif
diff --git a/drivers/usb/common/usb-otg.c b/drivers/usb/common/usb-otg.c
new file mode 100644
index 0000000..b5bdf62
--- /dev/null
+++ b/drivers/usb/common/usb-otg.c
@@ -0,0 +1,832 @@
+/**
+ * drivers/usb/common/usb-otg.c - USB OTG core
+ *
+ * Copyright (C) 2016 Texas Instruments Incorporated - http://www.ti.com
+ * Author: Roger Quadros <rogerq@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/usb/otg.h>
+#include <linux/usb/gadget.h>
+#include <linux/workqueue.h>
+
+/* OTG device list */
+LIST_HEAD(otg_list);
+static DEFINE_MUTEX(otg_list_mutex);
+
+static int usb_otg_hcd_is_primary_hcd(struct usb_hcd *hcd)
+{
+ if (!hcd->primary_hcd)
+ return 1;
+ return hcd == hcd->primary_hcd;
+}
+
+/**
+ * Check if the OTG device is in our OTG list and return
+ * usb_otg data, else NULL.
+ *
+ * otg_list_mutex must be held.
+ */
+static struct usb_otg *usb_otg_get_data(struct device *otg_dev)
+{
+ struct usb_otg *otg;
+
+ if (!otg_dev)
+ return NULL;
+
+ list_for_each_entry(otg, &otg_list, list) {
+ if (otg->dev == otg_dev)
+ return otg;
+ }
+
+ return NULL;
+}
+
+/**
+ * usb_otg_start_host - start/stop the host controller
+ * @otg: usb_otg instance
+ * @on: true to start, false to stop
+ *
+ * Start/stop the USB host controller. This function is meant
+ * for use by the OTG controller driver.
+ */
+int usb_otg_start_host(struct usb_otg *otg, int on)
+{
+ struct otg_hcd_ops *hcd_ops = otg->hcd_ops;
+
+ dev_dbg(otg->dev, "otg: %s %d\n", __func__, on);
+ if (!otg->host) {
+ WARN_ONCE(1, "otg: fsm running without host\n");
+ return 0;
+ }
+
+ if (on) {
+ if (otg->flags & OTG_FLAG_HOST_RUNNING)
+ return 0;
+
+ otg->flags |= OTG_FLAG_HOST_RUNNING;
+
+ /* start host */
+ hcd_ops->add(otg->primary_hcd.hcd, otg->primary_hcd.irqnum,
+ otg->primary_hcd.irqflags);
+ if (otg->shared_hcd.hcd) {
+ hcd_ops->add(otg->shared_hcd.hcd,
+ otg->shared_hcd.irqnum,
+ otg->shared_hcd.irqflags);
+ }
+ } else {
+ if (!(otg->flags & OTG_FLAG_HOST_RUNNING))
+ return 0;
+
+ otg->flags &= ~OTG_FLAG_HOST_RUNNING;
+
+ /* stop host */
+ if (otg->shared_hcd.hcd)
+ hcd_ops->remove(otg->shared_hcd.hcd);
+
+ hcd_ops->remove(otg->primary_hcd.hcd);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usb_otg_start_host);
+
+/**
+ * usb_otg_start_gadget - start/stop the gadget controller
+ * @otg: usb_otg instance
+ * @on: true to start, false to stop
+ *
+ * Start/stop the USB gadget controller. This function is meant
+ * for use by the OTG controller driver.
+ */
+int usb_otg_start_gadget(struct usb_otg *otg, int on)
+{
+ struct usb_gadget *gadget = otg->gadget;
+
+ dev_dbg(otg->dev, "otg: %s %d\n", __func__, on);
+ if (!gadget) {
+ WARN_ONCE(1, "otg: fsm running without gadget\n");
+ return 0;
+ }
+
+ if (on) {
+ if (otg->flags & OTG_FLAG_GADGET_RUNNING)
+ return 0;
+
+ otg->flags |= OTG_FLAG_GADGET_RUNNING;
+ otg->gadget_ops->start(otg->gadget);
+ } else {
+ if (!(otg->flags & OTG_FLAG_GADGET_RUNNING))
+ return 0;
+
+ otg->flags &= ~OTG_FLAG_GADGET_RUNNING;
+ otg->gadget_ops->stop(otg->gadget);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usb_otg_start_gadget);
+
+/**
+ * Change USB protocol when there is a protocol change.
+ * fsm->lock must be held.
+ */
+static int drd_set_protocol(struct otg_fsm *fsm, int protocol)
+{
+ struct usb_otg *otg = container_of(fsm, struct usb_otg, fsm);
+ int ret = 0;
+
+ if (fsm->protocol != protocol) {
+ dev_dbg(otg->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(otg, 0);
+ } else if (fsm->protocol == PROTO_GADGET) {
+ otg->gadget_ops->connect_control(otg->gadget, false);
+ ret = otg_start_gadget(otg, 0);
+ }
+
+ if (ret)
+ return ret;
+
+ /* start new protocol */
+ if (protocol == PROTO_HOST) {
+ ret = otg_start_host(otg, 1);
+ } else if (protocol == PROTO_GADGET) {
+ ret = otg_start_gadget(otg, 1);
+ otg->gadget_ops->connect_control(otg->gadget, true);
+ }
+
+ if (ret)
+ return ret;
+
+ fsm->protocol = protocol;
+ return 0;
+ }
+
+ return 0;
+}
+
+/**
+ * Called when entering a DRD state.
+ * fsm->lock must be held.
+ */
+static void drd_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state)
+{
+ struct usb_otg *otg = container_of(fsm, struct usb_otg, fsm);
+
+ if (otg->state == new_state)
+ return;
+
+ fsm->state_changed = 1;
+ dev_dbg(otg->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);
+ otg_drv_vbus(otg, 0);
+ break;
+ case OTG_STATE_B_PERIPHERAL:
+ drd_set_protocol(fsm, PROTO_GADGET);
+ otg_drv_vbus(otg, 0);
+ break;
+ case OTG_STATE_A_HOST:
+ drd_set_protocol(fsm, PROTO_HOST);
+ otg_drv_vbus(otg, 1);
+ break;
+ default:
+ dev_warn(otg->dev, "%s: otg: invalid state: %s\n",
+ __func__, usb_otg_state_string(new_state));
+ break;
+ }
+
+ otg->state = new_state;
+}
+
+/**
+ * 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->b_sess_vld
+ */
+int drd_statemachine(struct usb_otg *otg)
+{
+ struct otg_fsm *fsm = &otg->fsm;
+ enum usb_otg_state state;
+ int ret;
+
+ mutex_lock(&fsm->lock);
+
+ fsm->state_changed = 0;
+ state = otg->state;
+
+ switch (state) {
+ case OTG_STATE_UNDEFINED:
+ if (!fsm->id)
+ drd_set_state(fsm, OTG_STATE_A_HOST);
+ else if (fsm->id && fsm->b_sess_vld)
+ 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->b_sess_vld)
+ 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->b_sess_vld)
+ drd_set_state(fsm, OTG_STATE_B_IDLE);
+ break;
+ case OTG_STATE_A_HOST:
+ if (fsm->id && fsm->b_sess_vld)
+ drd_set_state(fsm, OTG_STATE_B_PERIPHERAL);
+ else if (fsm->id && !fsm->b_sess_vld)
+ drd_set_state(fsm, OTG_STATE_B_IDLE);
+ break;
+
+ default:
+ dev_err(otg->dev, "%s: otg: invalid usb-drd state: %s\n",
+ __func__, usb_otg_state_string(state));
+ break;
+ }
+
+ ret = fsm->state_changed;
+ mutex_unlock(&fsm->lock);
+ dev_dbg(otg->dev, "otg: quit statemachine, changed %d\n",
+ fsm->state_changed);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(drd_statemachine);
+
+/**
+ * Dual-role device (DRD) work function
+ */
+static void usb_drd_work(struct work_struct *work)
+{
+ struct usb_otg *otg = container_of(work, struct usb_otg, work);
+
+ pm_runtime_get_sync(otg->dev);
+ while (drd_statemachine(otg))
+ ;
+ pm_runtime_put_sync(otg->dev);
+}
+
+/**
+ * usb_otg_register() - Register the OTG/dual-role device to OTG core
+ * @dev: OTG/dual-role controller device.
+ * @config: OTG configuration.
+ *
+ * Registers the OTG/dual-role controller device with the USB OTG core.
+ *
+ * Return: struct usb_otg * if success, ERR_PTR() if error.
+ */
+struct usb_otg *usb_otg_register(struct device *dev,
+ struct usb_otg_config *config)
+{
+ struct usb_otg *otg;
+ int ret = 0;
+
+ if (!dev || !config || !config->fsm_ops)
+ return ERR_PTR(-EINVAL);
+
+ /* already in list? */
+ mutex_lock(&otg_list_mutex);
+ if (usb_otg_get_data(dev)) {
+ dev_err(dev, "otg: %s: device already in otg list\n",
+ __func__);
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ /* allocate and add to list */
+ otg = kzalloc(sizeof(*otg), GFP_KERNEL);
+ if (!otg) {
+ ret = -ENOMEM;
+ goto unlock;
+ }
+
+ otg->dev = dev;
+ /* otg->caps is controller caps + DT overrides */
+ otg->caps = *config->otg_caps;
+ ret = of_usb_update_otg_caps(dev->of_node, &otg->caps);
+ if (ret)
+ goto err_wq;
+
+ if ((otg->caps.hnp_support || otg->caps.srp_support ||
+ otg->caps.adp_support) && !config->otg_work) {
+ dev_err(dev,
+ "otg: otg_work must be provided for OTG support\n");
+ ret = -EINVAL;
+ goto err_wq;
+ }
+
+ if (config->otg_work) /* custom otg_work ? */
+ INIT_WORK(&otg->work, config->otg_work);
+ else
+ INIT_WORK(&otg->work, usb_drd_work);
+
+ otg->wq = create_freezable_workqueue("usb_otg");
+ if (!otg->wq) {
+ dev_err(dev, "otg: %s: can't create workqueue\n",
+ __func__);
+ ret = -ENOMEM;
+ goto err_wq;
+ }
+
+ /* set otg ops */
+ otg->fsm.ops = config->fsm_ops;
+
+ mutex_init(&otg->fsm.lock);
+
+ list_add_tail(&otg->list, &otg_list);
+ mutex_unlock(&otg_list_mutex);
+
+ return otg;
+
+err_wq:
+ kfree(otg);
+unlock:
+ mutex_unlock(&otg_list_mutex);
+ return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(usb_otg_register);
+
+/**
+ * usb_otg_unregister() - Unregister the OTG/dual-role device from USB OTG core
+ * @dev: OTG controller device.
+ *
+ * Unregisters the OTG/dual-role controller device from USB OTG core.
+ * Prevents unregistering till both the associated Host and Gadget controllers
+ * have unregistered from the OTG core.
+ *
+ * Return: 0 on success, error value otherwise.
+ */
+int usb_otg_unregister(struct device *dev)
+{
+ struct usb_otg *otg;
+
+ mutex_lock(&otg_list_mutex);
+ otg = usb_otg_get_data(dev);
+ if (!otg) {
+ dev_err(dev, "otg: %s: device not in otg list\n",
+ __func__);
+ mutex_unlock(&otg_list_mutex);
+ return -EINVAL;
+ }
+
+ /* prevent unregister till both host & gadget have unregistered */
+ if (otg->host || otg->gadget) {
+ dev_err(dev, "otg: %s: host/gadget still registered\n",
+ __func__);
+ return -EBUSY;
+ }
+
+ /* OTG FSM is halted when host/gadget unregistered */
+ destroy_workqueue(otg->wq);
+
+ /* remove from otg list */
+ list_del(&otg->list);
+ kfree(otg);
+ mutex_unlock(&otg_list_mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usb_otg_unregister);
+
+/**
+ * start/kick the OTG FSM if we can
+ * fsm->lock must be held
+ */
+static void usb_otg_start_fsm(struct usb_otg *otg)
+{
+ struct otg_fsm *fsm = &otg->fsm;
+
+ if (fsm->running)
+ goto kick_fsm;
+
+ if (!otg->host) {
+ dev_info(otg->dev, "otg: can't start till host registers\n");
+ return;
+ }
+
+ if (!otg->gadget) {
+ dev_info(otg->dev,
+ "otg: can't start till gadget UDC registers\n");
+ return;
+ }
+
+ if (!otg->gadget_ready) {
+ dev_info(otg->dev,
+ "otg: can't start till gadget function registers\n");
+ return;
+ }
+
+ fsm->running = true;
+kick_fsm:
+ queue_work(otg->wq, &otg->work);
+}
+
+/**
+ * stop the OTG FSM. Stops Host & Gadget controllers as well.
+ * fsm->lock must be held
+ */
+static void usb_otg_stop_fsm(struct usb_otg *otg)
+{
+ struct otg_fsm *fsm = &otg->fsm;
+
+ if (!fsm->running)
+ return;
+
+ /* no more new events queued */
+ fsm->running = false;
+
+ flush_workqueue(otg->wq);
+ otg->state = OTG_STATE_UNDEFINED;
+
+ /* stop host/gadget immediately */
+ if (fsm->protocol == PROTO_HOST) {
+ otg_start_host(otg, 0);
+ } else if (fsm->protocol == PROTO_GADGET) {
+ otg->gadget_ops->connect_control(otg->gadget, false);
+ otg_start_gadget(otg, 0);
+ }
+ fsm->protocol = PROTO_UNDEF;
+}
+
+/**
+ * usb_otg_sync_inputs - Sync OTG inputs with the OTG state machine
+ * @fsm: OTG FSM instance
+ *
+ * Used by the OTG driver to update the inputs to the OTG
+ * state machine.
+ *
+ * Can be called in IRQ context.
+ */
+void usb_otg_sync_inputs(struct usb_otg *otg)
+{
+ /* Don't kick FSM till it has started */
+ if (!otg->fsm.running)
+ return;
+
+ /* Kick FSM */
+ queue_work(otg->wq, &otg->work);
+}
+EXPORT_SYMBOL_GPL(usb_otg_sync_inputs);
+
+/**
+ * usb_otg_kick_fsm - Kick the OTG state machine
+ * @otg_dev: OTG controller device
+ *
+ * Used by USB host/device stack to sync OTG related
+ * events to the OTG state machine.
+ * e.g. change in host_bus->b_hnp_enable, gadget->b_hnp_enable
+ *
+ * Returns: 0 on success, error value otherwise.
+ */
+int usb_otg_kick_fsm(struct device *otg_dev)
+{
+ struct usb_otg *otg;
+
+ mutex_lock(&otg_list_mutex);
+ otg = usb_otg_get_data(otg_dev);
+ mutex_unlock(&otg_list_mutex);
+ if (!otg) {
+ dev_dbg(otg_dev, "otg: %s: invalid otg device\n",
+ __func__);
+ return -ENODEV;
+ }
+
+ usb_otg_sync_inputs(otg);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usb_otg_kick_fsm);
+
+/**
+ * usb_otg_register_hcd - Register the host controller to OTG core
+ * @hcd: host controller device
+ * @irqnum: interrupt number
+ * @irqflags: interrupt flags
+ * @ops: HCD ops to interface with the HCD
+ *
+ * This is used by the USB Host stack to register the host controller
+ * to the OTG core. Host controller must not be started by the
+ * caller as it is left upto the OTG state machine to do so.
+ * hcd->otg_dev must contain the related otg controller device.
+ *
+ * Returns: 0 on success, error value otherwise.
+ */
+int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
+ unsigned long irqflags, struct otg_hcd_ops *ops)
+{
+ struct usb_otg *otg;
+ struct device *hcd_dev = hcd->self.controller;
+ struct device *otg_dev = hcd->otg_dev;
+
+ if (!otg_dev)
+ return -EINVAL;
+
+ /* we're otg but otg controller might not yet be registered */
+ mutex_lock(&otg_list_mutex);
+ otg = usb_otg_get_data(otg_dev);
+ mutex_unlock(&otg_list_mutex);
+ if (!otg) {
+ dev_dbg(hcd_dev,
+ "otg: controller not yet registered. deferring.\n");
+ return -EPROBE_DEFER;
+ }
+
+ /* HCD will be started by OTG fsm when needed */
+ mutex_lock(&otg->fsm.lock);
+ if (otg->primary_hcd.hcd) {
+ /* probably a shared HCD ? */
+ if (usb_otg_hcd_is_primary_hcd(hcd)) {
+ dev_err(otg_dev, "otg: primary host already registered\n");
+ goto err;
+ }
+
+ if (hcd->shared_hcd == otg->primary_hcd.hcd) {
+ if (otg->shared_hcd.hcd) {
+ dev_err(otg_dev, "otg: shared host already registered\n");
+ goto err;
+ }
+
+ otg->shared_hcd.hcd = hcd;
+ otg->shared_hcd.irqnum = irqnum;
+ otg->shared_hcd.irqflags = irqflags;
+ otg->shared_hcd.ops = ops;
+ dev_info(otg_dev, "otg: shared host %s registered\n",
+ dev_name(hcd->self.controller));
+ } else {
+ dev_err(otg_dev, "otg: invalid shared host %s\n",
+ dev_name(hcd->self.controller));
+ goto err;
+ }
+ } else {
+ if (!usb_otg_hcd_is_primary_hcd(hcd)) {
+ dev_err(otg_dev, "otg: primary host must be registered first\n");
+ goto err;
+ }
+
+ otg->primary_hcd.hcd = hcd;
+ otg->primary_hcd.irqnum = irqnum;
+ otg->primary_hcd.irqflags = irqflags;
+ otg->primary_hcd.ops = ops;
+ otg->hcd_ops = ops;
+ dev_info(otg_dev, "otg: primary host %s registered\n",
+ dev_name(hcd->self.controller));
+ }
+
+ /*
+ * we're ready only if we have shared HCD
+ * or we don't need shared HCD.
+ */
+ if (otg->shared_hcd.hcd || !otg->primary_hcd.hcd->shared_hcd) {
+ otg->host = hcd_to_bus(hcd);
+ /* FIXME: set bus->otg_port if this is true OTG port with HNP */
+
+ /* start FSM */
+ usb_otg_start_fsm(otg);
+ } else {
+ dev_dbg(otg_dev, "otg: can't start till shared host registers\n");
+ }
+
+ mutex_unlock(&otg->fsm.lock);
+
+ return 0;
+
+err:
+ mutex_unlock(&otg->fsm.lock);
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(usb_otg_register_hcd);
+
+/**
+ * usb_otg_unregister_hcd - Unregister the host controller from OTG core
+ * @hcd: host controller device
+ *
+ * This is used by the USB Host stack to unregister the host controller
+ * from the OTG core. Ensures that host controller is not running
+ * on successful return.
+ *
+ * Returns: 0 on success, error value otherwise.
+ */
+int usb_otg_unregister_hcd(struct usb_hcd *hcd)
+{
+ struct usb_otg *otg;
+ struct device *hcd_dev = hcd_to_bus(hcd)->controller;
+ struct device *otg_dev = hcd->otg_dev;
+
+ if (!otg_dev)
+ return -EINVAL; /* we're definitely not OTG */
+
+ mutex_lock(&otg_list_mutex);
+ otg = usb_otg_get_data(otg_dev);
+ mutex_unlock(&otg_list_mutex);
+ if (!otg) {
+ dev_err(hcd_dev, "otg: host %s wasn't registered with otg\n",
+ dev_name(hcd_dev));
+ return -EINVAL;
+ }
+
+ mutex_lock(&otg->fsm.lock);
+ if (hcd == otg->primary_hcd.hcd) {
+ otg->primary_hcd.hcd = NULL;
+ dev_info(otg_dev, "otg: primary host %s unregistered\n",
+ dev_name(hcd_dev));
+ } else if (hcd == otg->shared_hcd.hcd) {
+ otg->shared_hcd.hcd = NULL;
+ dev_info(otg_dev, "otg: shared host %s unregistered\n",
+ dev_name(hcd_dev));
+ } else {
+ mutex_unlock(&otg->fsm.lock);
+ dev_err(otg_dev, "otg: host %s wasn't registered with otg\n",
+ dev_name(hcd_dev));
+ return -EINVAL;
+ }
+
+ /* stop FSM & Host */
+ usb_otg_stop_fsm(otg);
+ otg->host = NULL;
+
+ mutex_unlock(&otg->fsm.lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usb_otg_unregister_hcd);
+
+/**
+ * usb_otg_register_gadget - Register the gadget controller to OTG core
+ * @gadget: gadget controller
+ *
+ * This is used by the USB gadget stack to register the gadget controller
+ * to the OTG core. Gadget controller must not be started by the
+ * caller as it is left upto the OTG state machine to do so.
+ *
+ * Gadget core must call this only when all resources required for
+ * gadget controller to run are available.
+ * i.e. gadget function driver is available.
+ *
+ * Returns: 0 on success, error value otherwise.
+ */
+int usb_otg_register_gadget(struct usb_gadget *gadget,
+ struct otg_gadget_ops *ops)
+{
+ struct usb_otg *otg;
+ struct device *gadget_dev = &gadget->dev;
+ struct device *otg_dev = gadget->otg_dev;
+
+ if (!otg_dev)
+ return -EINVAL; /* we're definitely not OTG */
+
+ /* we're otg but otg controller might not yet be registered */
+ mutex_lock(&otg_list_mutex);
+ otg = usb_otg_get_data(otg_dev);
+ mutex_unlock(&otg_list_mutex);
+ if (!otg) {
+ dev_dbg(gadget_dev,
+ "otg: controller not yet registered, deferring.\n");
+ return -EPROBE_DEFER;
+ }
+
+ mutex_lock(&otg->fsm.lock);
+ if (otg->gadget) {
+ dev_err(otg_dev, "otg: gadget already registered with otg\n");
+ mutex_unlock(&otg->fsm.lock);
+ return -EINVAL;
+ }
+
+ otg->gadget = gadget;
+ otg->gadget_ops = ops;
+ dev_info(otg_dev, "otg: gadget %s registered\n",
+ dev_name(&gadget->dev));
+
+ /* FSM will be started in usb_otg_gadget_ready() */
+ mutex_unlock(&otg->fsm.lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usb_otg_register_gadget);
+
+/**
+ * usb_otg_unregister_gadget - Unregister the gadget controller from OTG core
+ * @gadget: gadget controller
+ *
+ * This is used by the USB gadget stack to unregister the gadget controller
+ * from the OTG core. Ensures that gadget controller is halted
+ * on successful return.
+ *
+ * Returns: 0 on success, error value otherwise.
+ */
+int usb_otg_unregister_gadget(struct usb_gadget *gadget)
+{
+ struct usb_otg *otg;
+ struct device *gadget_dev = &gadget->dev;
+ struct device *otg_dev = gadget->otg_dev;
+
+ if (!otg_dev)
+ return -EINVAL;
+
+ mutex_lock(&otg_list_mutex);
+ otg = usb_otg_get_data(otg_dev);
+ mutex_unlock(&otg_list_mutex);
+ if (!otg) {
+ dev_err(gadget_dev,
+ "otg: gadget %s wasn't registered with otg\n",
+ dev_name(&gadget->dev));
+ return -EINVAL;
+ }
+
+ mutex_lock(&otg->fsm.lock);
+ if (otg->gadget != gadget) {
+ mutex_unlock(&otg->fsm.lock);
+ dev_err(otg_dev, "otg: gadget %s wasn't registered with otg\n",
+ dev_name(&gadget->dev));
+ return -EINVAL;
+ }
+
+ /* FSM must be stopped in usb_otg_gadget_ready() */
+ if (otg->gadget_ready) {
+ dev_err(otg_dev,
+ "otg: gadget %s unregistered before being unready, forcing stop\n",
+ dev_name(&gadget->dev));
+ usb_otg_stop_fsm(otg);
+ }
+
+ otg->gadget = NULL;
+ mutex_unlock(&otg->fsm.lock);
+
+ dev_info(otg_dev, "otg: gadget %s unregistered\n",
+ dev_name(&gadget->dev));
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usb_otg_unregister_gadget);
+
+int usb_otg_gadget_ready(struct usb_gadget *gadget, bool ready)
+{
+ struct usb_otg *otg;
+ struct device *gadget_dev = &gadget->dev;
+ struct device *otg_dev = gadget->otg_dev;
+
+ if (!otg_dev)
+ return -EINVAL;
+
+ mutex_lock(&otg_list_mutex);
+ otg = usb_otg_get_data(otg_dev);
+ mutex_unlock(&otg_list_mutex);
+ if (!otg) {
+ dev_err(gadget_dev,
+ "otg: gadget %s wasn't registered with otg\n",
+ dev_name(&gadget->dev));
+ return -EINVAL;
+ }
+
+ mutex_lock(&otg->fsm.lock);
+ if (otg->gadget != gadget) {
+ mutex_unlock(&otg->fsm.lock);
+ dev_err(otg_dev, "otg: gadget %s wasn't registered with otg\n",
+ dev_name(&gadget->dev));
+ return -EINVAL;
+ }
+
+ /* Start/stop FSM & gadget */
+ otg->gadget_ready = ready;
+ if (ready)
+ usb_otg_start_fsm(otg);
+ else
+ usb_otg_stop_fsm(otg);
+
+ dev_dbg(otg_dev, "otg: gadget %s %sready\n", dev_name(&gadget->dev),
+ ready ? "" : "not ");
+
+ mutex_unlock(&otg->fsm.lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usb_otg_gadget_ready);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/core/Kconfig b/drivers/usb/core/Kconfig
index ae228d0..37f8c54 100644
--- a/drivers/usb/core/Kconfig
+++ b/drivers/usb/core/Kconfig
@@ -41,20 +41,6 @@ config USB_DYNAMIC_MINORS
If you are unsure about this, say N here.
-config USB_OTG
- bool "OTG support"
- depends on PM
- default n
- help
- The most notable feature of USB OTG is support for a
- "Dual-Role" device, which can act as either a device
- or a host. The initial role is decided by the type of
- plug inserted and can be changed later when two dual
- role devices talk to each other.
-
- Select this only if your board has Mini-AB/Micro-AB
- connector.
-
config USB_OTG_WHITELIST
bool "Rely on OTG and EH Targeted Peripherals List"
depends on USB
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 2057add..9d55384 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -16,6 +16,7 @@
menuconfig USB_GADGET
tristate "USB Gadget Support"
select USB_COMMON
+ select USB_OTG_CORE
select NLS
help
USB is a master/slave protocol, organized with one master
diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h
index 51e3bde..1237f66 100644
--- a/include/linux/usb/gadget.h
+++ b/include/linux/usb/gadget.h
@@ -583,6 +583,7 @@ struct usb_gadget_ops {
* @out_epnum: last used out ep number
* @in_epnum: last used in ep number
* @otg_caps: OTG capabilities of this gadget.
+ * @otg_dev: OTG controller device, if needs to be used with OTG core.
* @sg_supported: true if we can handle scatter-gather
* @is_otg: True if the USB device port uses a Mini-AB jack, so that the
* gadget driver must provide a USB OTG descriptor.
@@ -639,6 +640,7 @@ struct usb_gadget {
unsigned out_epnum;
unsigned in_epnum;
struct usb_otg_caps *otg_caps;
+ struct device *otg_dev;
unsigned sg_supported:1;
unsigned is_otg:1;
diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h
index 7914bed..845c761 100644
--- a/include/linux/usb/hcd.h
+++ b/include/linux/usb/hcd.h
@@ -185,6 +185,7 @@ struct usb_hcd {
struct mutex *bandwidth_mutex;
struct usb_hcd *shared_hcd;
struct usb_hcd *primary_hcd;
+ struct device *otg_dev; /* OTG controller device */
#define HCD_BUFFER_POOLS 4
diff --git a/include/linux/usb/otg-fsm.h b/include/linux/usb/otg-fsm.h
index 26e6531..943714a 100644
--- a/include/linux/usb/otg-fsm.h
+++ b/include/linux/usb/otg-fsm.h
@@ -60,6 +60,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.
+ * @b_sess_vld: VBUS voltage in regulation.
+ *
* OTG hardware Inputs
*
* Common inputs for A and B device
@@ -132,6 +137,7 @@ enum otg_fsm_timer {
* a_clr_err: Asserted (by application ?) to clear a_vbus_err due to an
* overcurrent condition and causes the A-device to transition
* to a_wait_vfall
+ * running: state machine running/stopped indicator
*/
struct otg_fsm {
/* Input */
@@ -187,6 +193,7 @@ struct otg_fsm {
int b_ase0_brst_tmout;
int a_bidl_adis_tmout;
+ bool running;
struct otg_fsm_ops *ops;
/* Current usb protocol used: 0:undefine; 1:host; 2:client */
diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h
index 85b8fb5..cc58a2d 100644
--- a/include/linux/usb/otg.h
+++ b/include/linux/usb/otg.h
@@ -10,10 +10,70 @@
#define __LINUX_USB_OTG_H
#include <linux/phy/phy.h>
-#include <linux/usb/phy.h>
-#include <linux/usb/otg-fsm.h>
+#include <linux/device.h>
+#include <linux/hrtimer.h>
+#include <linux/ktime.h>
+#include <linux/usb.h>
#include <linux/usb/hcd.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/otg-fsm.h>
+#include <linux/usb/phy.h>
+
+/**
+ * struct otg_hcd - host controller state and interface
+ *
+ * @hcd: host controller
+ * @irqnum: irq number
+ * @irqflags: irq flags
+ * @ops: otg to host controller interface
+ * @ops: otg to host controller interface
+ * @otg_dev: otg controller device
+ */
+struct otg_hcd {
+ struct usb_hcd *hcd;
+ unsigned int irqnum;
+ unsigned long irqflags;
+ struct otg_hcd_ops *ops;
+ struct device *otg_dev;
+};
+
+/**
+ * struct usb_otg_caps - describes the otg capabilities of the device
+ * @otg_rev: The OTG revision number the device is compliant with, it's
+ * in binary-coded decimal (i.e. 2.0 is 0200H).
+ * @hnp_support: Indicates if the device supports HNP.
+ * @srp_support: Indicates if the device supports SRP.
+ * @adp_support: Indicates if the device supports ADP.
+ */
+struct usb_otg_caps {
+ u16 otg_rev;
+ bool hnp_support;
+ bool srp_support;
+ bool adp_support;
+};
+/**
+ * struct usb_otg - usb otg controller state
+ *
+ * @default_a: Indicates we are an A device. i.e. Host.
+ * @phy: USB phy interface
+ * @usb_phy: old usb_phy interface
+ * @host: host controller bus
+ * @gadget: gadget device
+ * @state: current otg state
+ * @dev: otg controller device
+ * @caps: otg capabilities revision, hnp, srp, etc
+ * @fsm: otg finite state machine
+ * @hcd_ops: host controller interface
+ * ------- internal use only -------
+ * @primary_hcd: primary host state and interface
+ * @shared_hcd: shared host state and interface
+ * @gadget_ops: gadget controller interface
+ * @list: list of otg controllers
+ * @work: otg state machine work
+ * @wq: otg state machine work queue
+ * @flags: to track if host/gadget is running
+ */
struct usb_otg {
u8 default_a;
@@ -24,9 +84,25 @@ struct usb_otg {
struct usb_gadget *gadget;
enum usb_otg_state state;
+ struct device *dev;
+ struct usb_otg_caps caps;
struct otg_fsm fsm;
struct otg_hcd_ops *hcd_ops;
+ /* internal use only */
+ struct otg_hcd primary_hcd;
+ struct otg_hcd shared_hcd;
+ struct otg_gadget_ops *gadget_ops;
+ bool gadget_ready;
+ struct list_head list;
+ struct work_struct work;
+ struct workqueue_struct *wq;
+ u32 flags;
+#define OTG_FLAG_GADGET_RUNNING (1 << 0)
+#define OTG_FLAG_HOST_RUNNING (1 << 1)
+ /* use otg->fsm.lock for serializing access */
+
+/*------------- deprecated interface -----------------------------*/
/* bind/unbind the host controller */
int (*set_host)(struct usb_otg *otg, struct usb_bus *host);
@@ -42,26 +118,101 @@ struct usb_otg {
/* start or continue HNP role switch */
int (*start_hnp)(struct usb_otg *otg);
-
+/*---------------------------------------------------------------*/
};
/**
- * struct usb_otg_caps - describes the otg capabilities of the device
- * @otg_rev: The OTG revision number the device is compliant with, it's
- * in binary-coded decimal (i.e. 2.0 is 0200H).
- * @hnp_support: Indicates if the device supports HNP.
- * @srp_support: Indicates if the device supports SRP.
- * @adp_support: Indicates if the device supports ADP.
+ * struct usb_otg_config - otg controller configuration
+ * @caps: otg capabilities of the controller
+ * @ops: otg fsm operations
+ * @otg_work: optional custom otg state machine work function
*/
-struct usb_otg_caps {
- u16 otg_rev;
- bool hnp_support;
- bool srp_support;
- bool adp_support;
+struct usb_otg_config {
+ struct usb_otg_caps *otg_caps;
+ struct otg_fsm_ops *fsm_ops;
+ void (*otg_work)(struct work_struct *work);
};
extern const char *usb_otg_state_string(enum usb_otg_state state);
+#if IS_ENABLED(CONFIG_USB_OTG)
+struct usb_otg *usb_otg_register(struct device *dev,
+ struct usb_otg_config *config);
+int usb_otg_unregister(struct device *dev);
+int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
+ unsigned long irqflags, struct otg_hcd_ops *ops);
+int usb_otg_unregister_hcd(struct usb_hcd *hcd);
+int usb_otg_register_gadget(struct usb_gadget *gadget,
+ struct otg_gadget_ops *ops);
+int usb_otg_unregister_gadget(struct usb_gadget *gadget);
+void usb_otg_sync_inputs(struct usb_otg *otg);
+int usb_otg_kick_fsm(struct device *otg_dev);
+int usb_otg_start_host(struct usb_otg *otg, int on);
+int usb_otg_start_gadget(struct usb_otg *otg, int on);
+int usb_otg_gadget_ready(struct usb_gadget *gadget, bool ready);
+
+#else /* CONFIG_USB_OTG */
+
+static inline struct usb_otg *usb_otg_register(struct device *dev,
+ struct usb_otg_config *config)
+{
+ return ERR_PTR(-ENOTSUPP);
+}
+
+static inline int usb_otg_unregister(struct device *dev)
+{
+ return -ENOTSUPP;
+}
+
+static inline int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
+ unsigned long irqflags,
+ struct otg_hcd_ops *ops)
+{
+ return -ENOTSUPP;
+}
+
+static inline int usb_otg_unregister_hcd(struct usb_hcd *hcd)
+{
+ return -ENOTSUPP;
+}
+
+static inline int usb_otg_register_gadget(struct usb_gadget *gadget,
+ struct otg_gadget_ops *ops)
+{
+ return -ENOTSUPP;
+}
+
+static inline int usb_otg_unregister_gadget(struct usb_gadget *gadget)
+{
+ return -ENOTSUPP;
+}
+
+static inline void usb_otg_sync_inputs(struct usb_otg *otg)
+{
+}
+
+static inline int usb_otg_kick_fsm(struct device *otg_dev)
+{
+ return -ENOTSUPP;
+}
+
+static inline int usb_otg_start_host(struct usb_otg *otg, int on)
+{
+ return -ENOTSUPP;
+}
+
+static inline int usb_otg_start_gadget(struct usb_otg *otg, int on)
+{
+ return -ENOTSUPP;
+}
+
+static inline int usb_otg_gadget_ready(struct usb_gadget *gadget, bool ready)
+{
+ return -ENOTSUPP;
+}
+#endif /* CONFIG_USB_OTG */
+
+/*------------- deprecated interface -----------------------------*/
/* Context: can sleep */
static inline int
otg_start_hnp(struct usb_otg *otg)
@@ -113,6 +264,8 @@ otg_start_srp(struct usb_otg *otg)
return -ENOTSUPP;
}
+/*---------------------------------------------------------------*/
+
/* for OTG controller drivers (and maybe other stuff) */
extern int usb_bus_start_enum(struct usb_bus *bus, unsigned port_num);
@@ -237,4 +390,6 @@ static inline int otg_start_gadget(struct usb_otg *otg, int on)
return otg->fsm.ops->start_gadget(otg, on);
}
+int drd_statemachine(struct usb_otg *otg);
+
#endif /* __LINUX_USB_OTG_H */
--
2.7.4
[toc] | [prev] | [next] | [standalone]
| From | Peter Chen <hzpeterchen@gmail.com> |
|---|---|
| Date | 2016-06-08 12:00 +0200 |
| Subject | Re: [PATCH v9 08/14] usb: otg: add OTG/dual-role core |
| Message-ID | <rHIrf-6Wr-7@gated-at.bofh.it> |
| In reply to | #1417120 |
On Wed, Jun 08, 2016 at 12:03:40PM +0300, Roger Quadros wrote:
> +int usb_otg_unregister(struct device *dev)
> +{
> + struct usb_otg *otg;
> +
> + mutex_lock(&otg_list_mutex);
> + otg = usb_otg_get_data(dev);
> + if (!otg) {
> + dev_err(dev, "otg: %s: device not in otg list\n",
> + __func__);
> + mutex_unlock(&otg_list_mutex);
> + return -EINVAL;
> + }
> +
> + /* prevent unregister till both host & gadget have unregistered */
> + if (otg->host || otg->gadget) {
> + dev_err(dev, "otg: %s: host/gadget still registered\n",
> + __func__);
You need to call mutex_unlock here
> +
> +int usb_otg_gadget_ready(struct usb_gadget *gadget, bool ready)
> +{
What this API is for? Why need it in this version?
> + struct usb_otg *otg;
> + struct device *gadget_dev = &gadget->dev;
> + struct device *otg_dev = gadget->otg_dev;
> +
> + if (!otg_dev)
> + return -EINVAL;
> +
> + mutex_lock(&otg_list_mutex);
> + otg = usb_otg_get_data(otg_dev);
> + mutex_unlock(&otg_list_mutex);
> + if (!otg) {
> + dev_err(gadget_dev,
> + "otg: gadget %s wasn't registered with otg\n",
> + dev_name(&gadget->dev));
> + return -EINVAL;
> + }
> +
> + mutex_lock(&otg->fsm.lock);
> + if (otg->gadget != gadget) {
> + mutex_unlock(&otg->fsm.lock);
> + dev_err(otg_dev, "otg: gadget %s wasn't registered with otg\n",
> + dev_name(&gadget->dev));
> + return -EINVAL;
> + }
> +
> + /* Start/stop FSM & gadget */
> + otg->gadget_ready = ready;
> + if (ready)
> + usb_otg_start_fsm(otg);
> + else
> + usb_otg_stop_fsm(otg);
> +
> + dev_dbg(otg_dev, "otg: gadget %s %sready\n", dev_name(&gadget->dev),
> + ready ? "" : "not ");
> +
> + mutex_unlock(&otg->fsm.lock);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(usb_otg_gadget_ready);
> +
> --- a/include/linux/usb/otg.h
> +++ b/include/linux/usb/otg.h
> @@ -10,10 +10,70 @@
> #define __LINUX_USB_OTG_H
>
> #include <linux/phy/phy.h>
> -#include <linux/usb/phy.h>
> -#include <linux/usb/otg-fsm.h>
> +#include <linux/device.h>
> +#include <linux/hrtimer.h>
> +#include <linux/ktime.h>
The above two timer header files are not needed.
Peter
> +#include <linux/usb.h>
> #include <linux/usb/hcd.h>
> +#include <linux/usb/gadget.h>
> +#include <linux/usb/otg-fsm.h>
> +#include <linux/usb/phy.h>
> +
> +/**
> + * struct otg_hcd - host controller state and interface
> + *
> + * @hcd: host controller
> + * @irqnum: irq number
> + * @irqflags: irq flags
> + * @ops: otg to host controller interface
> + * @ops: otg to host controller interface
> + * @otg_dev: otg controller device
> + */
> +struct otg_hcd {
> + struct usb_hcd *hcd;
> + unsigned int irqnum;
> + unsigned long irqflags;
> + struct otg_hcd_ops *ops;
> + struct device *otg_dev;
> +};
> +
> +/**
> + * struct usb_otg_caps - describes the otg capabilities of the device
> + * @otg_rev: The OTG revision number the device is compliant with, it's
> + * in binary-coded decimal (i.e. 2.0 is 0200H).
> + * @hnp_support: Indicates if the device supports HNP.
> + * @srp_support: Indicates if the device supports SRP.
> + * @adp_support: Indicates if the device supports ADP.
> + */
> +struct usb_otg_caps {
> + u16 otg_rev;
> + bool hnp_support;
> + bool srp_support;
> + bool adp_support;
> +};
>
> +/**
> + * struct usb_otg - usb otg controller state
> + *
> + * @default_a: Indicates we are an A device. i.e. Host.
> + * @phy: USB phy interface
> + * @usb_phy: old usb_phy interface
> + * @host: host controller bus
> + * @gadget: gadget device
> + * @state: current otg state
> + * @dev: otg controller device
> + * @caps: otg capabilities revision, hnp, srp, etc
> + * @fsm: otg finite state machine
> + * @hcd_ops: host controller interface
> + * ------- internal use only -------
> + * @primary_hcd: primary host state and interface
> + * @shared_hcd: shared host state and interface
> + * @gadget_ops: gadget controller interface
> + * @list: list of otg controllers
> + * @work: otg state machine work
> + * @wq: otg state machine work queue
> + * @flags: to track if host/gadget is running
> + */
> struct usb_otg {
> u8 default_a;
>
> @@ -24,9 +84,25 @@ struct usb_otg {
> struct usb_gadget *gadget;
>
> enum usb_otg_state state;
> + struct device *dev;
> + struct usb_otg_caps caps;
> struct otg_fsm fsm;
> struct otg_hcd_ops *hcd_ops;
>
> + /* internal use only */
> + struct otg_hcd primary_hcd;
> + struct otg_hcd shared_hcd;
> + struct otg_gadget_ops *gadget_ops;
> + bool gadget_ready;
> + struct list_head list;
> + struct work_struct work;
> + struct workqueue_struct *wq;
> + u32 flags;
> +#define OTG_FLAG_GADGET_RUNNING (1 << 0)
> +#define OTG_FLAG_HOST_RUNNING (1 << 1)
> + /* use otg->fsm.lock for serializing access */
> +
> +/*------------- deprecated interface -----------------------------*/
> /* bind/unbind the host controller */
> int (*set_host)(struct usb_otg *otg, struct usb_bus *host);
>
> @@ -42,26 +118,101 @@ struct usb_otg {
>
> /* start or continue HNP role switch */
> int (*start_hnp)(struct usb_otg *otg);
> -
> +/*---------------------------------------------------------------*/
> };
>
> /**
> - * struct usb_otg_caps - describes the otg capabilities of the device
> - * @otg_rev: The OTG revision number the device is compliant with, it's
> - * in binary-coded decimal (i.e. 2.0 is 0200H).
> - * @hnp_support: Indicates if the device supports HNP.
> - * @srp_support: Indicates if the device supports SRP.
> - * @adp_support: Indicates if the device supports ADP.
> + * struct usb_otg_config - otg controller configuration
> + * @caps: otg capabilities of the controller
> + * @ops: otg fsm operations
> + * @otg_work: optional custom otg state machine work function
> */
> -struct usb_otg_caps {
> - u16 otg_rev;
> - bool hnp_support;
> - bool srp_support;
> - bool adp_support;
> +struct usb_otg_config {
> + struct usb_otg_caps *otg_caps;
> + struct otg_fsm_ops *fsm_ops;
> + void (*otg_work)(struct work_struct *work);
> };
>
> extern const char *usb_otg_state_string(enum usb_otg_state state);
>
> +#if IS_ENABLED(CONFIG_USB_OTG)
> +struct usb_otg *usb_otg_register(struct device *dev,
> + struct usb_otg_config *config);
> +int usb_otg_unregister(struct device *dev);
> +int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
> + unsigned long irqflags, struct otg_hcd_ops *ops);
> +int usb_otg_unregister_hcd(struct usb_hcd *hcd);
> +int usb_otg_register_gadget(struct usb_gadget *gadget,
> + struct otg_gadget_ops *ops);
> +int usb_otg_unregister_gadget(struct usb_gadget *gadget);
> +void usb_otg_sync_inputs(struct usb_otg *otg);
> +int usb_otg_kick_fsm(struct device *otg_dev);
> +int usb_otg_start_host(struct usb_otg *otg, int on);
> +int usb_otg_start_gadget(struct usb_otg *otg, int on);
> +int usb_otg_gadget_ready(struct usb_gadget *gadget, bool ready);
> +
> +#else /* CONFIG_USB_OTG */
> +
> +static inline struct usb_otg *usb_otg_register(struct device *dev,
> + struct usb_otg_config *config)
> +{
> + return ERR_PTR(-ENOTSUPP);
> +}
> +
> +static inline int usb_otg_unregister(struct device *dev)
> +{
> + return -ENOTSUPP;
> +}
> +
> +static inline int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
> + unsigned long irqflags,
> + struct otg_hcd_ops *ops)
> +{
> + return -ENOTSUPP;
> +}
> +
> +static inline int usb_otg_unregister_hcd(struct usb_hcd *hcd)
> +{
> + return -ENOTSUPP;
> +}
> +
> +static inline int usb_otg_register_gadget(struct usb_gadget *gadget,
> + struct otg_gadget_ops *ops)
> +{
> + return -ENOTSUPP;
> +}
> +
> +static inline int usb_otg_unregister_gadget(struct usb_gadget *gadget)
> +{
> + return -ENOTSUPP;
> +}
> +
> +static inline void usb_otg_sync_inputs(struct usb_otg *otg)
> +{
> +}
> +
> +static inline int usb_otg_kick_fsm(struct device *otg_dev)
> +{
> + return -ENOTSUPP;
> +}
> +
> +static inline int usb_otg_start_host(struct usb_otg *otg, int on)
> +{
> + return -ENOTSUPP;
> +}
> +
> +static inline int usb_otg_start_gadget(struct usb_otg *otg, int on)
> +{
> + return -ENOTSUPP;
> +}
> +
> +static inline int usb_otg_gadget_ready(struct usb_gadget *gadget, bool ready)
> +{
> + return -ENOTSUPP;
> +}
> +#endif /* CONFIG_USB_OTG */
> +
> +/*------------- deprecated interface -----------------------------*/
> /* Context: can sleep */
> static inline int
> otg_start_hnp(struct usb_otg *otg)
> @@ -113,6 +264,8 @@ otg_start_srp(struct usb_otg *otg)
> return -ENOTSUPP;
> }
>
> +/*---------------------------------------------------------------*/
> +
> /* for OTG controller drivers (and maybe other stuff) */
> extern int usb_bus_start_enum(struct usb_bus *bus, unsigned port_num);
>
> @@ -237,4 +390,6 @@ static inline int otg_start_gadget(struct usb_otg *otg, int on)
> return otg->fsm.ops->start_gadget(otg, on);
> }
>
> +int drd_statemachine(struct usb_otg *otg);
> +
> #endif /* __LINUX_USB_OTG_H */
> --
> 2.7.4
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-usb" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
--
Best Regards,
Peter Chen
[toc] | [prev] | [next] | [standalone]
| From | Roger Quadros <rogerq@ti.com> |
|---|---|
| Date | 2016-06-08 12:20 +0200 |
| Subject | Re: [PATCH v9 08/14] usb: otg: add OTG/dual-role core |
| Message-ID | <rHIKC-7iL-21@gated-at.bofh.it> |
| In reply to | #1417210 |
Hi,
On 08/06/16 12:53, Peter Chen wrote:
> On Wed, Jun 08, 2016 at 12:03:40PM +0300, Roger Quadros wrote:
>> +int usb_otg_unregister(struct device *dev)
>> +{
>> + struct usb_otg *otg;
>> +
>> + mutex_lock(&otg_list_mutex);
>> + otg = usb_otg_get_data(dev);
>> + if (!otg) {
>> + dev_err(dev, "otg: %s: device not in otg list\n",
>> + __func__);
>> + mutex_unlock(&otg_list_mutex);
>> + return -EINVAL;
>> + }
>> +
>> + /* prevent unregister till both host & gadget have unregistered */
>> + if (otg->host || otg->gadget) {
>> + dev_err(dev, "otg: %s: host/gadget still registered\n",
>> + __func__);
>
> You need to call mutex_unlock here
Indeed. good catch.
>
>> +
>> +int usb_otg_gadget_ready(struct usb_gadget *gadget, bool ready)
>> +{
>
> What this API is for? Why need it in this version?
we moved gadget to otg registration from udc_bind_to_driver() to usb_add_gadget_udc_release().
This means there is a window when gadget function driver (e.g. g_zero) is not loaded.
We don't want to start the gadget controller in that window.
usb_otg_gadget_ready() is used by gadget core to notify the otg core when the function driver
is ready or not-ready.
>
>> + struct usb_otg *otg;
>> + struct device *gadget_dev = &gadget->dev;
>> + struct device *otg_dev = gadget->otg_dev;
>> +
>> + if (!otg_dev)
>> + return -EINVAL;
>> +
>> + mutex_lock(&otg_list_mutex);
>> + otg = usb_otg_get_data(otg_dev);
>> + mutex_unlock(&otg_list_mutex);
>> + if (!otg) {
>> + dev_err(gadget_dev,
>> + "otg: gadget %s wasn't registered with otg\n",
>> + dev_name(&gadget->dev));
>> + return -EINVAL;
>> + }
>> +
>> + mutex_lock(&otg->fsm.lock);
>> + if (otg->gadget != gadget) {
>> + mutex_unlock(&otg->fsm.lock);
>> + dev_err(otg_dev, "otg: gadget %s wasn't registered with otg\n",
>> + dev_name(&gadget->dev));
>> + return -EINVAL;
>> + }
>> +
>> + /* Start/stop FSM & gadget */
>> + otg->gadget_ready = ready;
>> + if (ready)
>> + usb_otg_start_fsm(otg);
>> + else
>> + usb_otg_stop_fsm(otg);
>> +
>> + dev_dbg(otg_dev, "otg: gadget %s %sready\n", dev_name(&gadget->dev),
>> + ready ? "" : "not ");
>> +
>> + mutex_unlock(&otg->fsm.lock);
>> +
>> + return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(usb_otg_gadget_ready);
>> +
>> --- a/include/linux/usb/otg.h
>> +++ b/include/linux/usb/otg.h
>> @@ -10,10 +10,70 @@
>> #define __LINUX_USB_OTG_H
>>
>> #include <linux/phy/phy.h>
>> -#include <linux/usb/phy.h>
>> -#include <linux/usb/otg-fsm.h>
>> +#include <linux/device.h>
>> +#include <linux/hrtimer.h>
>> +#include <linux/ktime.h>
>
> The above two timer header files are not needed.
You had pointed this out earlier, but I missed it. Sorry.
--
cheers,
-roger
[toc] | [prev] | [next] | [standalone]
| From | Peter Chen <hzpeterchen@gmail.com> |
|---|---|
| Date | 2016-06-08 14:50 +0200 |
| Subject | Re: [PATCH v9 08/14] usb: otg: add OTG/dual-role core |
| Message-ID | <rHL5L-aT-5@gated-at.bofh.it> |
| In reply to | #1417249 |
On Wed, Jun 08, 2016 at 01:12:10PM +0300, Roger Quadros wrote:
> Hi,
>
> On 08/06/16 12:53, Peter Chen wrote:
> > On Wed, Jun 08, 2016 at 12:03:40PM +0300, Roger Quadros wrote:
> >> +int usb_otg_unregister(struct device *dev)
> >> +{
> >> + struct usb_otg *otg;
> >> +
> >> + mutex_lock(&otg_list_mutex);
> >> + otg = usb_otg_get_data(dev);
> >> + if (!otg) {
> >> + dev_err(dev, "otg: %s: device not in otg list\n",
> >> + __func__);
> >> + mutex_unlock(&otg_list_mutex);
> >> + return -EINVAL;
> >> + }
> >> +
> >> + /* prevent unregister till both host & gadget have unregistered */
> >> + if (otg->host || otg->gadget) {
> >> + dev_err(dev, "otg: %s: host/gadget still registered\n",
> >> + __func__);
> >
> > You need to call mutex_unlock here
>
> Indeed. good catch.
> >
> >> +
> >> +int usb_otg_gadget_ready(struct usb_gadget *gadget, bool ready)
> >> +{
> >
> > What this API is for? Why need it in this version?
>
> we moved gadget to otg registration from udc_bind_to_driver() to usb_add_gadget_udc_release().
> This means there is a window when gadget function driver (e.g. g_zero) is not loaded.
> We don't want to start the gadget controller in that window.
>
> usb_otg_gadget_ready() is used by gadget core to notify the otg core when the function driver
> is ready or not-ready.
>
Why you need to move this from gadget's probe to udc's probe? Currently,
the sequence of gadget and udc's probe is random, but udc_bind_to_driver
is called when udc is ready.
--
Best Regards,
Peter Chen
[toc] | [prev] | [next] | [standalone]
| From | Roger Quadros <rogerq@ti.com> |
|---|---|
| Date | 2016-06-09 09:30 +0200 |
| Subject | Re: [PATCH v9 08/14] usb: otg: add OTG/dual-role core |
| Message-ID | <rI2zE-3dZ-17@gated-at.bofh.it> |
| In reply to | #1417374 |
On 08/06/16 15:42, Peter Chen wrote:
> On Wed, Jun 08, 2016 at 01:12:10PM +0300, Roger Quadros wrote:
>> Hi,
>>
>> On 08/06/16 12:53, Peter Chen wrote:
>>> On Wed, Jun 08, 2016 at 12:03:40PM +0300, Roger Quadros wrote:
>>>> +int usb_otg_unregister(struct device *dev)
>>>> +{
>>>> + struct usb_otg *otg;
>>>> +
>>>> + mutex_lock(&otg_list_mutex);
>>>> + otg = usb_otg_get_data(dev);
>>>> + if (!otg) {
>>>> + dev_err(dev, "otg: %s: device not in otg list\n",
>>>> + __func__);
>>>> + mutex_unlock(&otg_list_mutex);
>>>> + return -EINVAL;
>>>> + }
>>>> +
>>>> + /* prevent unregister till both host & gadget have unregistered */
>>>> + if (otg->host || otg->gadget) {
>>>> + dev_err(dev, "otg: %s: host/gadget still registered\n",
>>>> + __func__);
>>>
>>> You need to call mutex_unlock here
>>
>> Indeed. good catch.
>>>
>>>> +
>>>> +int usb_otg_gadget_ready(struct usb_gadget *gadget, bool ready)
>>>> +{
>>>
>>> What this API is for? Why need it in this version?
>>
>> we moved gadget to otg registration from udc_bind_to_driver() to usb_add_gadget_udc_release().
>> This means there is a window when gadget function driver (e.g. g_zero) is not loaded.
>> We don't want to start the gadget controller in that window.
>>
>> usb_otg_gadget_ready() is used by gadget core to notify the otg core when the function driver
>> is ready or not-ready.
>>
>
> Why you need to move this from gadget's probe to udc's probe? Currently,
> the sequence of gadget and udc's probe is random, but udc_bind_to_driver
> is called when udc is ready.
>
Because we need to support -EPROBE_DEFER at udc probe.
At udc_bind_to_driver there is no scope for -EPROBE_DEFER.
Another issue is that we need to register with otg and have otg->caps available
before driver->bind() is called in udc_bind_to_driver(). Else gadget function
will not have the correct OTG descriptor.
cheers,
-roger
[toc] | [prev] | [next] | [standalone]
| From | Roger Quadros <rogerq@ti.com> |
|---|---|
| Date | 2016-06-09 10:00 +0200 |
| Subject | [PATCH v10 08/14] usb: otg: add OTG/dual-role core |
| Message-ID | <rI32G-3oT-27@gated-at.bofh.it> |
| In reply to | #1417120 |
It provides APIs for the following tasks
- Registering an OTG/dual-role capable controller
- Registering Host and Gadget controllers to OTG core
- Providing inputs to and kicking the OTG state machine
Provide a dual-role device (DRD) state machine.
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 'b_sess_vld'.
Signed-off-by: Roger Quadros <rogerq@ti.com>
---
v10:
- added missing mutex_unlock in in failure path in usb_otg_unregister()
- removed unnecessary hrtimer.h and ktime.h in linux/usb/otg.h
drivers/usb/Kconfig | 18 +
drivers/usb/Makefile | 1 +
drivers/usb/common/Makefile | 6 +-
drivers/usb/common/usb-otg.c | 833 +++++++++++++++++++++++++++++++++++++++++++
drivers/usb/core/Kconfig | 14 -
drivers/usb/gadget/Kconfig | 1 +
include/linux/usb/gadget.h | 2 +
include/linux/usb/hcd.h | 1 +
include/linux/usb/otg-fsm.h | 7 +
include/linux/usb/otg.h | 181 +++++++++-
10 files changed, 1034 insertions(+), 30 deletions(-)
create mode 100644 drivers/usb/common/usb-otg.c
diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index 8689dcb..ed596ec 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -32,6 +32,23 @@ if USB_SUPPORT
config USB_COMMON
tristate
+config USB_OTG_CORE
+ tristate
+
+config USB_OTG
+ bool "OTG/Dual-role support"
+ depends on PM && USB && USB_GADGET
+ default n
+ ---help---
+ The most notable feature of USB OTG is support for a
+ "Dual-Role" device, which can act as either a device
+ or a host. The initial role is decided by the type of
+ plug inserted and can be changed later when two dual
+ role devices talk to each other.
+
+ Select this only if your board has Mini-AB/Micro-AB
+ connector.
+
config USB_ARCH_HAS_HCD
def_bool y
@@ -40,6 +57,7 @@ config USB
tristate "Support for Host-side USB"
depends on USB_ARCH_HAS_HCD
select USB_COMMON
+ select USB_OTG_CORE
select NLS # for UTF-8 strings
---help---
Universal Serial Bus (USB) is a specification for a serial bus
diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
index dca7856..03f7204 100644
--- a/drivers/usb/Makefile
+++ b/drivers/usb/Makefile
@@ -59,5 +59,6 @@ obj-$(CONFIG_USB_RENESAS_USBHS) += renesas_usbhs/
obj-$(CONFIG_USB_GADGET) += gadget/
obj-$(CONFIG_USB_COMMON) += common/
+obj-$(CONFIG_USB_OTG_CORE) += common/
obj-$(CONFIG_USBIP_CORE) += usbip/
diff --git a/drivers/usb/common/Makefile b/drivers/usb/common/Makefile
index f8f2c88..5122b3f 100644
--- a/drivers/usb/common/Makefile
+++ b/drivers/usb/common/Makefile
@@ -7,5 +7,7 @@ usb-common-y += common.o
usb-common-$(CONFIG_USB_LED_TRIG) += led.o
obj-$(CONFIG_USB_ULPI_BUS) += ulpi.o
-usbotg-y := usb-otg-fsm.o
-obj-$(CONFIG_USB_OTG) += usbotg.o
+ifeq ($(CONFIG_USB_OTG),y)
+usbotg-y := usb-otg.o usb-otg-fsm.o
+obj-$(CONFIG_USB_OTG_CORE) += usbotg.o
+endif
diff --git a/drivers/usb/common/usb-otg.c b/drivers/usb/common/usb-otg.c
new file mode 100644
index 0000000..baebe5c
--- /dev/null
+++ b/drivers/usb/common/usb-otg.c
@@ -0,0 +1,833 @@
+/**
+ * drivers/usb/common/usb-otg.c - USB OTG core
+ *
+ * Copyright (C) 2016 Texas Instruments Incorporated - http://www.ti.com
+ * Author: Roger Quadros <rogerq@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/usb/otg.h>
+#include <linux/usb/gadget.h>
+#include <linux/workqueue.h>
+
+/* OTG device list */
+LIST_HEAD(otg_list);
+static DEFINE_MUTEX(otg_list_mutex);
+
+static int usb_otg_hcd_is_primary_hcd(struct usb_hcd *hcd)
+{
+ if (!hcd->primary_hcd)
+ return 1;
+ return hcd == hcd->primary_hcd;
+}
+
+/**
+ * Check if the OTG device is in our OTG list and return
+ * usb_otg data, else NULL.
+ *
+ * otg_list_mutex must be held.
+ */
+static struct usb_otg *usb_otg_get_data(struct device *otg_dev)
+{
+ struct usb_otg *otg;
+
+ if (!otg_dev)
+ return NULL;
+
+ list_for_each_entry(otg, &otg_list, list) {
+ if (otg->dev == otg_dev)
+ return otg;
+ }
+
+ return NULL;
+}
+
+/**
+ * usb_otg_start_host - start/stop the host controller
+ * @otg: usb_otg instance
+ * @on: true to start, false to stop
+ *
+ * Start/stop the USB host controller. This function is meant
+ * for use by the OTG controller driver.
+ */
+int usb_otg_start_host(struct usb_otg *otg, int on)
+{
+ struct otg_hcd_ops *hcd_ops = otg->hcd_ops;
+
+ dev_dbg(otg->dev, "otg: %s %d\n", __func__, on);
+ if (!otg->host) {
+ WARN_ONCE(1, "otg: fsm running without host\n");
+ return 0;
+ }
+
+ if (on) {
+ if (otg->flags & OTG_FLAG_HOST_RUNNING)
+ return 0;
+
+ otg->flags |= OTG_FLAG_HOST_RUNNING;
+
+ /* start host */
+ hcd_ops->add(otg->primary_hcd.hcd, otg->primary_hcd.irqnum,
+ otg->primary_hcd.irqflags);
+ if (otg->shared_hcd.hcd) {
+ hcd_ops->add(otg->shared_hcd.hcd,
+ otg->shared_hcd.irqnum,
+ otg->shared_hcd.irqflags);
+ }
+ } else {
+ if (!(otg->flags & OTG_FLAG_HOST_RUNNING))
+ return 0;
+
+ otg->flags &= ~OTG_FLAG_HOST_RUNNING;
+
+ /* stop host */
+ if (otg->shared_hcd.hcd)
+ hcd_ops->remove(otg->shared_hcd.hcd);
+
+ hcd_ops->remove(otg->primary_hcd.hcd);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usb_otg_start_host);
+
+/**
+ * usb_otg_start_gadget - start/stop the gadget controller
+ * @otg: usb_otg instance
+ * @on: true to start, false to stop
+ *
+ * Start/stop the USB gadget controller. This function is meant
+ * for use by the OTG controller driver.
+ */
+int usb_otg_start_gadget(struct usb_otg *otg, int on)
+{
+ struct usb_gadget *gadget = otg->gadget;
+
+ dev_dbg(otg->dev, "otg: %s %d\n", __func__, on);
+ if (!gadget) {
+ WARN_ONCE(1, "otg: fsm running without gadget\n");
+ return 0;
+ }
+
+ if (on) {
+ if (otg->flags & OTG_FLAG_GADGET_RUNNING)
+ return 0;
+
+ otg->flags |= OTG_FLAG_GADGET_RUNNING;
+ otg->gadget_ops->start(otg->gadget);
+ } else {
+ if (!(otg->flags & OTG_FLAG_GADGET_RUNNING))
+ return 0;
+
+ otg->flags &= ~OTG_FLAG_GADGET_RUNNING;
+ otg->gadget_ops->stop(otg->gadget);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usb_otg_start_gadget);
+
+/**
+ * Change USB protocol when there is a protocol change.
+ * fsm->lock must be held.
+ */
+static int drd_set_protocol(struct otg_fsm *fsm, int protocol)
+{
+ struct usb_otg *otg = container_of(fsm, struct usb_otg, fsm);
+ int ret = 0;
+
+ if (fsm->protocol != protocol) {
+ dev_dbg(otg->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(otg, 0);
+ } else if (fsm->protocol == PROTO_GADGET) {
+ otg->gadget_ops->connect_control(otg->gadget, false);
+ ret = otg_start_gadget(otg, 0);
+ }
+
+ if (ret)
+ return ret;
+
+ /* start new protocol */
+ if (protocol == PROTO_HOST) {
+ ret = otg_start_host(otg, 1);
+ } else if (protocol == PROTO_GADGET) {
+ ret = otg_start_gadget(otg, 1);
+ otg->gadget_ops->connect_control(otg->gadget, true);
+ }
+
+ if (ret)
+ return ret;
+
+ fsm->protocol = protocol;
+ return 0;
+ }
+
+ return 0;
+}
+
+/**
+ * Called when entering a DRD state.
+ * fsm->lock must be held.
+ */
+static void drd_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state)
+{
+ struct usb_otg *otg = container_of(fsm, struct usb_otg, fsm);
+
+ if (otg->state == new_state)
+ return;
+
+ fsm->state_changed = 1;
+ dev_dbg(otg->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);
+ otg_drv_vbus(otg, 0);
+ break;
+ case OTG_STATE_B_PERIPHERAL:
+ drd_set_protocol(fsm, PROTO_GADGET);
+ otg_drv_vbus(otg, 0);
+ break;
+ case OTG_STATE_A_HOST:
+ drd_set_protocol(fsm, PROTO_HOST);
+ otg_drv_vbus(otg, 1);
+ break;
+ default:
+ dev_warn(otg->dev, "%s: otg: invalid state: %s\n",
+ __func__, usb_otg_state_string(new_state));
+ break;
+ }
+
+ otg->state = new_state;
+}
+
+/**
+ * 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->b_sess_vld
+ */
+int drd_statemachine(struct usb_otg *otg)
+{
+ struct otg_fsm *fsm = &otg->fsm;
+ enum usb_otg_state state;
+ int ret;
+
+ mutex_lock(&fsm->lock);
+
+ fsm->state_changed = 0;
+ state = otg->state;
+
+ switch (state) {
+ case OTG_STATE_UNDEFINED:
+ if (!fsm->id)
+ drd_set_state(fsm, OTG_STATE_A_HOST);
+ else if (fsm->id && fsm->b_sess_vld)
+ 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->b_sess_vld)
+ 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->b_sess_vld)
+ drd_set_state(fsm, OTG_STATE_B_IDLE);
+ break;
+ case OTG_STATE_A_HOST:
+ if (fsm->id && fsm->b_sess_vld)
+ drd_set_state(fsm, OTG_STATE_B_PERIPHERAL);
+ else if (fsm->id && !fsm->b_sess_vld)
+ drd_set_state(fsm, OTG_STATE_B_IDLE);
+ break;
+
+ default:
+ dev_err(otg->dev, "%s: otg: invalid usb-drd state: %s\n",
+ __func__, usb_otg_state_string(state));
+ break;
+ }
+
+ ret = fsm->state_changed;
+ mutex_unlock(&fsm->lock);
+ dev_dbg(otg->dev, "otg: quit statemachine, changed %d\n",
+ fsm->state_changed);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(drd_statemachine);
+
+/**
+ * Dual-role device (DRD) work function
+ */
+static void usb_drd_work(struct work_struct *work)
+{
+ struct usb_otg *otg = container_of(work, struct usb_otg, work);
+
+ pm_runtime_get_sync(otg->dev);
+ while (drd_statemachine(otg))
+ ;
+ pm_runtime_put_sync(otg->dev);
+}
+
+/**
+ * usb_otg_register() - Register the OTG/dual-role device to OTG core
+ * @dev: OTG/dual-role controller device.
+ * @config: OTG configuration.
+ *
+ * Registers the OTG/dual-role controller device with the USB OTG core.
+ *
+ * Return: struct usb_otg * if success, ERR_PTR() if error.
+ */
+struct usb_otg *usb_otg_register(struct device *dev,
+ struct usb_otg_config *config)
+{
+ struct usb_otg *otg;
+ int ret = 0;
+
+ if (!dev || !config || !config->fsm_ops)
+ return ERR_PTR(-EINVAL);
+
+ /* already in list? */
+ mutex_lock(&otg_list_mutex);
+ if (usb_otg_get_data(dev)) {
+ dev_err(dev, "otg: %s: device already in otg list\n",
+ __func__);
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ /* allocate and add to list */
+ otg = kzalloc(sizeof(*otg), GFP_KERNEL);
+ if (!otg) {
+ ret = -ENOMEM;
+ goto unlock;
+ }
+
+ otg->dev = dev;
+ /* otg->caps is controller caps + DT overrides */
+ otg->caps = *config->otg_caps;
+ ret = of_usb_update_otg_caps(dev->of_node, &otg->caps);
+ if (ret)
+ goto err_wq;
+
+ if ((otg->caps.hnp_support || otg->caps.srp_support ||
+ otg->caps.adp_support) && !config->otg_work) {
+ dev_err(dev,
+ "otg: otg_work must be provided for OTG support\n");
+ ret = -EINVAL;
+ goto err_wq;
+ }
+
+ if (config->otg_work) /* custom otg_work ? */
+ INIT_WORK(&otg->work, config->otg_work);
+ else
+ INIT_WORK(&otg->work, usb_drd_work);
+
+ otg->wq = create_freezable_workqueue("usb_otg");
+ if (!otg->wq) {
+ dev_err(dev, "otg: %s: can't create workqueue\n",
+ __func__);
+ ret = -ENOMEM;
+ goto err_wq;
+ }
+
+ /* set otg ops */
+ otg->fsm.ops = config->fsm_ops;
+
+ mutex_init(&otg->fsm.lock);
+
+ list_add_tail(&otg->list, &otg_list);
+ mutex_unlock(&otg_list_mutex);
+
+ return otg;
+
+err_wq:
+ kfree(otg);
+unlock:
+ mutex_unlock(&otg_list_mutex);
+ return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(usb_otg_register);
+
+/**
+ * usb_otg_unregister() - Unregister the OTG/dual-role device from USB OTG core
+ * @dev: OTG controller device.
+ *
+ * Unregisters the OTG/dual-role controller device from USB OTG core.
+ * Prevents unregistering till both the associated Host and Gadget controllers
+ * have unregistered from the OTG core.
+ *
+ * Return: 0 on success, error value otherwise.
+ */
+int usb_otg_unregister(struct device *dev)
+{
+ struct usb_otg *otg;
+
+ mutex_lock(&otg_list_mutex);
+ otg = usb_otg_get_data(dev);
+ if (!otg) {
+ dev_err(dev, "otg: %s: device not in otg list\n",
+ __func__);
+ mutex_unlock(&otg_list_mutex);
+ return -EINVAL;
+ }
+
+ /* prevent unregister till both host & gadget have unregistered */
+ if (otg->host || otg->gadget) {
+ dev_err(dev, "otg: %s: host/gadget still registered\n",
+ __func__);
+ mutex_unlock(&otg_list_mutex);
+ return -EBUSY;
+ }
+
+ /* OTG FSM is halted when host/gadget unregistered */
+ destroy_workqueue(otg->wq);
+
+ /* remove from otg list */
+ list_del(&otg->list);
+ kfree(otg);
+ mutex_unlock(&otg_list_mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usb_otg_unregister);
+
+/**
+ * start/kick the OTG FSM if we can
+ * fsm->lock must be held
+ */
+static void usb_otg_start_fsm(struct usb_otg *otg)
+{
+ struct otg_fsm *fsm = &otg->fsm;
+
+ if (fsm->running)
+ goto kick_fsm;
+
+ if (!otg->host) {
+ dev_info(otg->dev, "otg: can't start till host registers\n");
+ return;
+ }
+
+ if (!otg->gadget) {
+ dev_info(otg->dev,
+ "otg: can't start till gadget UDC registers\n");
+ return;
+ }
+
+ if (!otg->gadget_ready) {
+ dev_info(otg->dev,
+ "otg: can't start till gadget function registers\n");
+ return;
+ }
+
+ fsm->running = true;
+kick_fsm:
+ queue_work(otg->wq, &otg->work);
+}
+
+/**
+ * stop the OTG FSM. Stops Host & Gadget controllers as well.
+ * fsm->lock must be held
+ */
+static void usb_otg_stop_fsm(struct usb_otg *otg)
+{
+ struct otg_fsm *fsm = &otg->fsm;
+
+ if (!fsm->running)
+ return;
+
+ /* no more new events queued */
+ fsm->running = false;
+
+ flush_workqueue(otg->wq);
+ otg->state = OTG_STATE_UNDEFINED;
+
+ /* stop host/gadget immediately */
+ if (fsm->protocol == PROTO_HOST) {
+ otg_start_host(otg, 0);
+ } else if (fsm->protocol == PROTO_GADGET) {
+ otg->gadget_ops->connect_control(otg->gadget, false);
+ otg_start_gadget(otg, 0);
+ }
+ fsm->protocol = PROTO_UNDEF;
+}
+
+/**
+ * usb_otg_sync_inputs - Sync OTG inputs with the OTG state machine
+ * @fsm: OTG FSM instance
+ *
+ * Used by the OTG driver to update the inputs to the OTG
+ * state machine.
+ *
+ * Can be called in IRQ context.
+ */
+void usb_otg_sync_inputs(struct usb_otg *otg)
+{
+ /* Don't kick FSM till it has started */
+ if (!otg->fsm.running)
+ return;
+
+ /* Kick FSM */
+ queue_work(otg->wq, &otg->work);
+}
+EXPORT_SYMBOL_GPL(usb_otg_sync_inputs);
+
+/**
+ * usb_otg_kick_fsm - Kick the OTG state machine
+ * @otg_dev: OTG controller device
+ *
+ * Used by USB host/device stack to sync OTG related
+ * events to the OTG state machine.
+ * e.g. change in host_bus->b_hnp_enable, gadget->b_hnp_enable
+ *
+ * Returns: 0 on success, error value otherwise.
+ */
+int usb_otg_kick_fsm(struct device *otg_dev)
+{
+ struct usb_otg *otg;
+
+ mutex_lock(&otg_list_mutex);
+ otg = usb_otg_get_data(otg_dev);
+ mutex_unlock(&otg_list_mutex);
+ if (!otg) {
+ dev_dbg(otg_dev, "otg: %s: invalid otg device\n",
+ __func__);
+ return -ENODEV;
+ }
+
+ usb_otg_sync_inputs(otg);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usb_otg_kick_fsm);
+
+/**
+ * usb_otg_register_hcd - Register the host controller to OTG core
+ * @hcd: host controller device
+ * @irqnum: interrupt number
+ * @irqflags: interrupt flags
+ * @ops: HCD ops to interface with the HCD
+ *
+ * This is used by the USB Host stack to register the host controller
+ * to the OTG core. Host controller must not be started by the
+ * caller as it is left upto the OTG state machine to do so.
+ * hcd->otg_dev must contain the related otg controller device.
+ *
+ * Returns: 0 on success, error value otherwise.
+ */
+int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
+ unsigned long irqflags, struct otg_hcd_ops *ops)
+{
+ struct usb_otg *otg;
+ struct device *hcd_dev = hcd->self.controller;
+ struct device *otg_dev = hcd->otg_dev;
+
+ if (!otg_dev)
+ return -EINVAL;
+
+ /* we're otg but otg controller might not yet be registered */
+ mutex_lock(&otg_list_mutex);
+ otg = usb_otg_get_data(otg_dev);
+ mutex_unlock(&otg_list_mutex);
+ if (!otg) {
+ dev_dbg(hcd_dev,
+ "otg: controller not yet registered. deferring.\n");
+ return -EPROBE_DEFER;
+ }
+
+ /* HCD will be started by OTG fsm when needed */
+ mutex_lock(&otg->fsm.lock);
+ if (otg->primary_hcd.hcd) {
+ /* probably a shared HCD ? */
+ if (usb_otg_hcd_is_primary_hcd(hcd)) {
+ dev_err(otg_dev, "otg: primary host already registered\n");
+ goto err;
+ }
+
+ if (hcd->shared_hcd == otg->primary_hcd.hcd) {
+ if (otg->shared_hcd.hcd) {
+ dev_err(otg_dev, "otg: shared host already registered\n");
+ goto err;
+ }
+
+ otg->shared_hcd.hcd = hcd;
+ otg->shared_hcd.irqnum = irqnum;
+ otg->shared_hcd.irqflags = irqflags;
+ otg->shared_hcd.ops = ops;
+ dev_info(otg_dev, "otg: shared host %s registered\n",
+ dev_name(hcd->self.controller));
+ } else {
+ dev_err(otg_dev, "otg: invalid shared host %s\n",
+ dev_name(hcd->self.controller));
+ goto err;
+ }
+ } else {
+ if (!usb_otg_hcd_is_primary_hcd(hcd)) {
+ dev_err(otg_dev, "otg: primary host must be registered first\n");
+ goto err;
+ }
+
+ otg->primary_hcd.hcd = hcd;
+ otg->primary_hcd.irqnum = irqnum;
+ otg->primary_hcd.irqflags = irqflags;
+ otg->primary_hcd.ops = ops;
+ otg->hcd_ops = ops;
+ dev_info(otg_dev, "otg: primary host %s registered\n",
+ dev_name(hcd->self.controller));
+ }
+
+ /*
+ * we're ready only if we have shared HCD
+ * or we don't need shared HCD.
+ */
+ if (otg->shared_hcd.hcd || !otg->primary_hcd.hcd->shared_hcd) {
+ otg->host = hcd_to_bus(hcd);
+ /* FIXME: set bus->otg_port if this is true OTG port with HNP */
+
+ /* start FSM */
+ usb_otg_start_fsm(otg);
+ } else {
+ dev_dbg(otg_dev, "otg: can't start till shared host registers\n");
+ }
+
+ mutex_unlock(&otg->fsm.lock);
+
+ return 0;
+
+err:
+ mutex_unlock(&otg->fsm.lock);
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(usb_otg_register_hcd);
+
+/**
+ * usb_otg_unregister_hcd - Unregister the host controller from OTG core
+ * @hcd: host controller device
+ *
+ * This is used by the USB Host stack to unregister the host controller
+ * from the OTG core. Ensures that host controller is not running
+ * on successful return.
+ *
+ * Returns: 0 on success, error value otherwise.
+ */
+int usb_otg_unregister_hcd(struct usb_hcd *hcd)
+{
+ struct usb_otg *otg;
+ struct device *hcd_dev = hcd_to_bus(hcd)->controller;
+ struct device *otg_dev = hcd->otg_dev;
+
+ if (!otg_dev)
+ return -EINVAL; /* we're definitely not OTG */
+
+ mutex_lock(&otg_list_mutex);
+ otg = usb_otg_get_data(otg_dev);
+ mutex_unlock(&otg_list_mutex);
+ if (!otg) {
+ dev_err(hcd_dev, "otg: host %s wasn't registered with otg\n",
+ dev_name(hcd_dev));
+ return -EINVAL;
+ }
+
+ mutex_lock(&otg->fsm.lock);
+ if (hcd == otg->primary_hcd.hcd) {
+ otg->primary_hcd.hcd = NULL;
+ dev_info(otg_dev, "otg: primary host %s unregistered\n",
+ dev_name(hcd_dev));
+ } else if (hcd == otg->shared_hcd.hcd) {
+ otg->shared_hcd.hcd = NULL;
+ dev_info(otg_dev, "otg: shared host %s unregistered\n",
+ dev_name(hcd_dev));
+ } else {
+ mutex_unlock(&otg->fsm.lock);
+ dev_err(otg_dev, "otg: host %s wasn't registered with otg\n",
+ dev_name(hcd_dev));
+ return -EINVAL;
+ }
+
+ /* stop FSM & Host */
+ usb_otg_stop_fsm(otg);
+ otg->host = NULL;
+
+ mutex_unlock(&otg->fsm.lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usb_otg_unregister_hcd);
+
+/**
+ * usb_otg_register_gadget - Register the gadget controller to OTG core
+ * @gadget: gadget controller
+ *
+ * This is used by the USB gadget stack to register the gadget controller
+ * to the OTG core. Gadget controller must not be started by the
+ * caller as it is left upto the OTG state machine to do so.
+ *
+ * Gadget core must call this only when all resources required for
+ * gadget controller to run are available.
+ * i.e. gadget function driver is available.
+ *
+ * Returns: 0 on success, error value otherwise.
+ */
+int usb_otg_register_gadget(struct usb_gadget *gadget,
+ struct otg_gadget_ops *ops)
+{
+ struct usb_otg *otg;
+ struct device *gadget_dev = &gadget->dev;
+ struct device *otg_dev = gadget->otg_dev;
+
+ if (!otg_dev)
+ return -EINVAL; /* we're definitely not OTG */
+
+ /* we're otg but otg controller might not yet be registered */
+ mutex_lock(&otg_list_mutex);
+ otg = usb_otg_get_data(otg_dev);
+ mutex_unlock(&otg_list_mutex);
+ if (!otg) {
+ dev_dbg(gadget_dev,
+ "otg: controller not yet registered, deferring.\n");
+ return -EPROBE_DEFER;
+ }
+
+ mutex_lock(&otg->fsm.lock);
+ if (otg->gadget) {
+ dev_err(otg_dev, "otg: gadget already registered with otg\n");
+ mutex_unlock(&otg->fsm.lock);
+ return -EINVAL;
+ }
+
+ otg->gadget = gadget;
+ otg->gadget_ops = ops;
+ dev_info(otg_dev, "otg: gadget %s registered\n",
+ dev_name(&gadget->dev));
+
+ /* FSM will be started in usb_otg_gadget_ready() */
+ mutex_unlock(&otg->fsm.lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usb_otg_register_gadget);
+
+/**
+ * usb_otg_unregister_gadget - Unregister the gadget controller from OTG core
+ * @gadget: gadget controller
+ *
+ * This is used by the USB gadget stack to unregister the gadget controller
+ * from the OTG core. Ensures that gadget controller is halted
+ * on successful return.
+ *
+ * Returns: 0 on success, error value otherwise.
+ */
+int usb_otg_unregister_gadget(struct usb_gadget *gadget)
+{
+ struct usb_otg *otg;
+ struct device *gadget_dev = &gadget->dev;
+ struct device *otg_dev = gadget->otg_dev;
+
+ if (!otg_dev)
+ return -EINVAL;
+
+ mutex_lock(&otg_list_mutex);
+ otg = usb_otg_get_data(otg_dev);
+ mutex_unlock(&otg_list_mutex);
+ if (!otg) {
+ dev_err(gadget_dev,
+ "otg: gadget %s wasn't registered with otg\n",
+ dev_name(&gadget->dev));
+ return -EINVAL;
+ }
+
+ mutex_lock(&otg->fsm.lock);
+ if (otg->gadget != gadget) {
+ mutex_unlock(&otg->fsm.lock);
+ dev_err(otg_dev, "otg: gadget %s wasn't registered with otg\n",
+ dev_name(&gadget->dev));
+ return -EINVAL;
+ }
+
+ /* FSM must be stopped in usb_otg_gadget_ready() */
+ if (otg->gadget_ready) {
+ dev_err(otg_dev,
+ "otg: gadget %s unregistered before being unready, forcing stop\n",
+ dev_name(&gadget->dev));
+ usb_otg_stop_fsm(otg);
+ }
+
+ otg->gadget = NULL;
+ mutex_unlock(&otg->fsm.lock);
+
+ dev_info(otg_dev, "otg: gadget %s unregistered\n",
+ dev_name(&gadget->dev));
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usb_otg_unregister_gadget);
+
+int usb_otg_gadget_ready(struct usb_gadget *gadget, bool ready)
+{
+ struct usb_otg *otg;
+ struct device *gadget_dev = &gadget->dev;
+ struct device *otg_dev = gadget->otg_dev;
+
+ if (!otg_dev)
+ return -EINVAL;
+
+ mutex_lock(&otg_list_mutex);
+ otg = usb_otg_get_data(otg_dev);
+ mutex_unlock(&otg_list_mutex);
+ if (!otg) {
+ dev_err(gadget_dev,
+ "otg: gadget %s wasn't registered with otg\n",
+ dev_name(&gadget->dev));
+ return -EINVAL;
+ }
+
+ mutex_lock(&otg->fsm.lock);
+ if (otg->gadget != gadget) {
+ mutex_unlock(&otg->fsm.lock);
+ dev_err(otg_dev, "otg: gadget %s wasn't registered with otg\n",
+ dev_name(&gadget->dev));
+ return -EINVAL;
+ }
+
+ /* Start/stop FSM & gadget */
+ otg->gadget_ready = ready;
+ if (ready)
+ usb_otg_start_fsm(otg);
+ else
+ usb_otg_stop_fsm(otg);
+
+ dev_dbg(otg_dev, "otg: gadget %s %sready\n", dev_name(&gadget->dev),
+ ready ? "" : "not ");
+
+ mutex_unlock(&otg->fsm.lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usb_otg_gadget_ready);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/core/Kconfig b/drivers/usb/core/Kconfig
index ae228d0..37f8c54 100644
--- a/drivers/usb/core/Kconfig
+++ b/drivers/usb/core/Kconfig
@@ -41,20 +41,6 @@ config USB_DYNAMIC_MINORS
If you are unsure about this, say N here.
-config USB_OTG
- bool "OTG support"
- depends on PM
- default n
- help
- The most notable feature of USB OTG is support for a
- "Dual-Role" device, which can act as either a device
- or a host. The initial role is decided by the type of
- plug inserted and can be changed later when two dual
- role devices talk to each other.
-
- Select this only if your board has Mini-AB/Micro-AB
- connector.
-
config USB_OTG_WHITELIST
bool "Rely on OTG and EH Targeted Peripherals List"
depends on USB
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 2057add..9d55384 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -16,6 +16,7 @@
menuconfig USB_GADGET
tristate "USB Gadget Support"
select USB_COMMON
+ select USB_OTG_CORE
select NLS
help
USB is a master/slave protocol, organized with one master
diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h
index 51e3bde..1237f66 100644
--- a/include/linux/usb/gadget.h
+++ b/include/linux/usb/gadget.h
@@ -583,6 +583,7 @@ struct usb_gadget_ops {
* @out_epnum: last used out ep number
* @in_epnum: last used in ep number
* @otg_caps: OTG capabilities of this gadget.
+ * @otg_dev: OTG controller device, if needs to be used with OTG core.
* @sg_supported: true if we can handle scatter-gather
* @is_otg: True if the USB device port uses a Mini-AB jack, so that the
* gadget driver must provide a USB OTG descriptor.
@@ -639,6 +640,7 @@ struct usb_gadget {
unsigned out_epnum;
unsigned in_epnum;
struct usb_otg_caps *otg_caps;
+ struct device *otg_dev;
unsigned sg_supported:1;
unsigned is_otg:1;
diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h
index 7729c1f..36bd54f 100644
--- a/include/linux/usb/hcd.h
+++ b/include/linux/usb/hcd.h
@@ -185,6 +185,7 @@ struct usb_hcd {
struct mutex *bandwidth_mutex;
struct usb_hcd *shared_hcd;
struct usb_hcd *primary_hcd;
+ struct device *otg_dev; /* OTG controller device */
#define HCD_BUFFER_POOLS 4
diff --git a/include/linux/usb/otg-fsm.h b/include/linux/usb/otg-fsm.h
index 26e6531..943714a 100644
--- a/include/linux/usb/otg-fsm.h
+++ b/include/linux/usb/otg-fsm.h
@@ -60,6 +60,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.
+ * @b_sess_vld: VBUS voltage in regulation.
+ *
* OTG hardware Inputs
*
* Common inputs for A and B device
@@ -132,6 +137,7 @@ enum otg_fsm_timer {
* a_clr_err: Asserted (by application ?) to clear a_vbus_err due to an
* overcurrent condition and causes the A-device to transition
* to a_wait_vfall
+ * running: state machine running/stopped indicator
*/
struct otg_fsm {
/* Input */
@@ -187,6 +193,7 @@ struct otg_fsm {
int b_ase0_brst_tmout;
int a_bidl_adis_tmout;
+ bool running;
struct otg_fsm_ops *ops;
/* Current usb protocol used: 0:undefine; 1:host; 2:client */
diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h
index 85b8fb5..5d4850a 100644
--- a/include/linux/usb/otg.h
+++ b/include/linux/usb/otg.h
@@ -10,10 +10,68 @@
#define __LINUX_USB_OTG_H
#include <linux/phy/phy.h>
-#include <linux/usb/phy.h>
-#include <linux/usb/otg-fsm.h>
+#include <linux/device.h>
+#include <linux/usb.h>
#include <linux/usb/hcd.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/otg-fsm.h>
+#include <linux/usb/phy.h>
+
+/**
+ * struct otg_hcd - host controller state and interface
+ *
+ * @hcd: host controller
+ * @irqnum: irq number
+ * @irqflags: irq flags
+ * @ops: otg to host controller interface
+ * @ops: otg to host controller interface
+ * @otg_dev: otg controller device
+ */
+struct otg_hcd {
+ struct usb_hcd *hcd;
+ unsigned int irqnum;
+ unsigned long irqflags;
+ struct otg_hcd_ops *ops;
+ struct device *otg_dev;
+};
+
+/**
+ * struct usb_otg_caps - describes the otg capabilities of the device
+ * @otg_rev: The OTG revision number the device is compliant with, it's
+ * in binary-coded decimal (i.e. 2.0 is 0200H).
+ * @hnp_support: Indicates if the device supports HNP.
+ * @srp_support: Indicates if the device supports SRP.
+ * @adp_support: Indicates if the device supports ADP.
+ */
+struct usb_otg_caps {
+ u16 otg_rev;
+ bool hnp_support;
+ bool srp_support;
+ bool adp_support;
+};
+/**
+ * struct usb_otg - usb otg controller state
+ *
+ * @default_a: Indicates we are an A device. i.e. Host.
+ * @phy: USB phy interface
+ * @usb_phy: old usb_phy interface
+ * @host: host controller bus
+ * @gadget: gadget device
+ * @state: current otg state
+ * @dev: otg controller device
+ * @caps: otg capabilities revision, hnp, srp, etc
+ * @fsm: otg finite state machine
+ * @hcd_ops: host controller interface
+ * ------- internal use only -------
+ * @primary_hcd: primary host state and interface
+ * @shared_hcd: shared host state and interface
+ * @gadget_ops: gadget controller interface
+ * @list: list of otg controllers
+ * @work: otg state machine work
+ * @wq: otg state machine work queue
+ * @flags: to track if host/gadget is running
+ */
struct usb_otg {
u8 default_a;
@@ -24,9 +82,25 @@ struct usb_otg {
struct usb_gadget *gadget;
enum usb_otg_state state;
+ struct device *dev;
+ struct usb_otg_caps caps;
struct otg_fsm fsm;
struct otg_hcd_ops *hcd_ops;
+ /* internal use only */
+ struct otg_hcd primary_hcd;
+ struct otg_hcd shared_hcd;
+ struct otg_gadget_ops *gadget_ops;
+ bool gadget_ready;
+ struct list_head list;
+ struct work_struct work;
+ struct workqueue_struct *wq;
+ u32 flags;
+#define OTG_FLAG_GADGET_RUNNING (1 << 0)
+#define OTG_FLAG_HOST_RUNNING (1 << 1)
+ /* use otg->fsm.lock for serializing access */
+
+/*------------- deprecated interface -----------------------------*/
/* bind/unbind the host controller */
int (*set_host)(struct usb_otg *otg, struct usb_bus *host);
@@ -42,26 +116,101 @@ struct usb_otg {
/* start or continue HNP role switch */
int (*start_hnp)(struct usb_otg *otg);
-
+/*---------------------------------------------------------------*/
};
/**
- * struct usb_otg_caps - describes the otg capabilities of the device
- * @otg_rev: The OTG revision number the device is compliant with, it's
- * in binary-coded decimal (i.e. 2.0 is 0200H).
- * @hnp_support: Indicates if the device supports HNP.
- * @srp_support: Indicates if the device supports SRP.
- * @adp_support: Indicates if the device supports ADP.
+ * struct usb_otg_config - otg controller configuration
+ * @caps: otg capabilities of the controller
+ * @ops: otg fsm operations
+ * @otg_work: optional custom otg state machine work function
*/
-struct usb_otg_caps {
- u16 otg_rev;
- bool hnp_support;
- bool srp_support;
- bool adp_support;
+struct usb_otg_config {
+ struct usb_otg_caps *otg_caps;
+ struct otg_fsm_ops *fsm_ops;
+ void (*otg_work)(struct work_struct *work);
};
extern const char *usb_otg_state_string(enum usb_otg_state state);
+#if IS_ENABLED(CONFIG_USB_OTG)
+struct usb_otg *usb_otg_register(struct device *dev,
+ struct usb_otg_config *config);
+int usb_otg_unregister(struct device *dev);
+int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
+ unsigned long irqflags, struct otg_hcd_ops *ops);
+int usb_otg_unregister_hcd(struct usb_hcd *hcd);
+int usb_otg_register_gadget(struct usb_gadget *gadget,
+ struct otg_gadget_ops *ops);
+int usb_otg_unregister_gadget(struct usb_gadget *gadget);
+void usb_otg_sync_inputs(struct usb_otg *otg);
+int usb_otg_kick_fsm(struct device *otg_dev);
+int usb_otg_start_host(struct usb_otg *otg, int on);
+int usb_otg_start_gadget(struct usb_otg *otg, int on);
+int usb_otg_gadget_ready(struct usb_gadget *gadget, bool ready);
+
+#else /* CONFIG_USB_OTG */
+
+static inline struct usb_otg *usb_otg_register(struct device *dev,
+ struct usb_otg_config *config)
+{
+ return ERR_PTR(-ENOTSUPP);
+}
+
+static inline int usb_otg_unregister(struct device *dev)
+{
+ return -ENOTSUPP;
+}
+
+static inline int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
+ unsigned long irqflags,
+ struct otg_hcd_ops *ops)
+{
+ return -ENOTSUPP;
+}
+
+static inline int usb_otg_unregister_hcd(struct usb_hcd *hcd)
+{
+ return -ENOTSUPP;
+}
+
+static inline int usb_otg_register_gadget(struct usb_gadget *gadget,
+ struct otg_gadget_ops *ops)
+{
+ return -ENOTSUPP;
+}
+
+static inline int usb_otg_unregister_gadget(struct usb_gadget *gadget)
+{
+ return -ENOTSUPP;
+}
+
+static inline void usb_otg_sync_inputs(struct usb_otg *otg)
+{
+}
+
+static inline int usb_otg_kick_fsm(struct device *otg_dev)
+{
+ return -ENOTSUPP;
+}
+
+static inline int usb_otg_start_host(struct usb_otg *otg, int on)
+{
+ return -ENOTSUPP;
+}
+
+static inline int usb_otg_start_gadget(struct usb_otg *otg, int on)
+{
+ return -ENOTSUPP;
+}
+
+static inline int usb_otg_gadget_ready(struct usb_gadget *gadget, bool ready)
+{
+ return -ENOTSUPP;
+}
+#endif /* CONFIG_USB_OTG */
+
+/*------------- deprecated interface -----------------------------*/
/* Context: can sleep */
static inline int
otg_start_hnp(struct usb_otg *otg)
@@ -113,6 +262,8 @@ otg_start_srp(struct usb_otg *otg)
return -ENOTSUPP;
}
+/*---------------------------------------------------------------*/
+
/* for OTG controller drivers (and maybe other stuff) */
extern int usb_bus_start_enum(struct usb_bus *bus, unsigned port_num);
@@ -237,4 +388,6 @@ static inline int otg_start_gadget(struct usb_otg *otg, int on)
return otg->fsm.ops->start_gadget(otg, on);
}
+int drd_statemachine(struct usb_otg *otg);
+
#endif /* __LINUX_USB_OTG_H */
--
2.7.4
[toc] | [prev] | [next] | [standalone]
| From | Sergei Shtylyov <sergei.shtylyov@cogentembedded.com> |
|---|---|
| Date | 2016-06-09 14:40 +0200 |
| Subject | Re: [PATCH v10 08/14] usb: otg: add OTG/dual-role core |
| Message-ID | <rI7pE-6lh-19@gated-at.bofh.it> |
| In reply to | #1418149 |
On 6/9/2016 10:53 AM, Roger Quadros wrote:
> It provides APIs for the following tasks
>
> - Registering an OTG/dual-role capable controller
> - Registering Host and Gadget controllers to OTG core
> - Providing inputs to and kicking the OTG state machine
>
> Provide a dual-role device (DRD) state machine.
> 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 'b_sess_vld'.
>
> Signed-off-by: Roger Quadros <rogerq@ti.com>
[...]
> diff --git a/drivers/usb/common/usb-otg.c b/drivers/usb/common/usb-otg.c
> new file mode 100644
> index 0000000..baebe5c
> --- /dev/null
> +++ b/drivers/usb/common/usb-otg.c
> @@ -0,0 +1,833 @@
[...]
> +/**
> + * Change USB protocol when there is a protocol change.
> + * fsm->lock must be held.
> + */
If you're using the kernel-doc comment, please follow the rules.
> +static int drd_set_protocol(struct otg_fsm *fsm, int protocol)
[...]
> +/**
> + * Called when entering a DRD state.
> + * fsm->lock must be held.
> + */
Same here.
> +static void drd_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state)
[...]
> +/**
> + * 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->b_sess_vld
> + */
And here.
> +/**
> + * Dual-role device (DRD) work function
> + */
And here.
> +static void usb_drd_work(struct work_struct *work)
> +{
> + struct usb_otg *otg = container_of(work, struct usb_otg, work);
> +
> + pm_runtime_get_sync(otg->dev);
> + while (drd_statemachine(otg))
> + ;
Indent it more please.
[...]
> +/**
> + * start/kick the OTG FSM if we can
> + * fsm->lock must be held
> + */
Please follow the kernel-doc rules.
[...]
> +/**
> + * stop the OTG FSM. Stops Host & Gadget controllers as well.
> + * fsm->lock must be held
> + */
Likewise.
[...]
> +/**
> + * usb_otg_sync_inputs - Sync OTG inputs with the OTG state machine
> + * @fsm: OTG FSM instance
Doesn't match the prototype.
> + *
> + * Used by the OTG driver to update the inputs to the OTG
> + * state machine.
> + *
> + * Can be called in IRQ context.
> + */
> +void usb_otg_sync_inputs(struct usb_otg *otg)
[...]
> +/**
> + * usb_otg_register_gadget - Register the gadget controller to OTG core
> + * @gadget: gadget controller
We call that USB device controller (UDC). I'm not sure what you meant here...
And what about the 2nd arg, 'ops'?
[...]
> diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h
> index 85b8fb5..5d4850a 100644
> --- a/include/linux/usb/otg.h
> +++ b/include/linux/usb/otg.h
> @@ -10,10 +10,68 @@
> #define __LINUX_USB_OTG_H
>
> #include <linux/phy/phy.h>
> -#include <linux/usb/phy.h>
> -#include <linux/usb/otg-fsm.h>
> +#include <linux/device.h>
> +#include <linux/usb.h>
> #include <linux/usb/hcd.h>
> +#include <linux/usb/gadget.h>
> +#include <linux/usb/otg-fsm.h>
> +#include <linux/usb/phy.h>
> +
> +/**
> + * struct otg_hcd - host controller state and interface
> + *
> + * @hcd: host controller
> + * @irqnum: irq number
> + * @irqflags: irq flags
IRQ?
> + * @ops: otg to host controller interface
> + * @ops: otg to host controller interface
Once is enough. :-)
> + * @otg_dev: otg controller device
OTG?
> +/**
> + * struct usb_otg - usb otg controller state
> + *
> + * @default_a: Indicates we are an A device. i.e. Host.
> + * @phy: USB phy interface
PHY?
> + * @usb_phy: old usb_phy interface
> + * @host: host controller bus
> + * @gadget: gadget device
> + * @state: current otg state
> + * @dev: otg controller device
> + * @caps: otg capabilities revision, hnp, srp, etc
> + * @fsm: otg finite state machine
OTG?
> + * @hcd_ops: host controller interface
> + * ------- internal use only -------
> + * @primary_hcd: primary host state and interface
> + * @shared_hcd: shared host state and interface
> + * @gadget_ops: gadget controller interface
> + * @list: list of otg controllers
> + * @work: otg state machine work
> + * @wq: otg state machine work queue
> + * @flags: to track if host/gadget is running
> + */
> struct usb_otg {
> u8 default_a;
>
[...]
> @@ -42,26 +116,101 @@ struct usb_otg {
>
> /* start or continue HNP role switch */
> int (*start_hnp)(struct usb_otg *otg);
> -
> +/*---------------------------------------------------------------*/
> };
>
> /**
> - * struct usb_otg_caps - describes the otg capabilities of the device
> - * @otg_rev: The OTG revision number the device is compliant with, it's
> - * in binary-coded decimal (i.e. 2.0 is 0200H).
> - * @hnp_support: Indicates if the device supports HNP.
> - * @srp_support: Indicates if the device supports SRP.
> - * @adp_support: Indicates if the device supports ADP.
> + * struct usb_otg_config - otg controller configuration
> + * @caps: otg capabilities of the controller
> + * @ops: otg fsm operations
OTG FSM?
> + * @otg_work: optional custom otg state machine work function
OTG?
> */
[...]
Phew, that was a long patch... normally I don't review the patches that
are such big. :-)
MBR, Sergei
[toc] | [prev] | [next] | [standalone]
| From | Roger Quadros <rogerq@ti.com> |
|---|---|
| Date | 2016-06-10 09:10 +0200 |
| Subject | Re: [PATCH v10 08/14] usb: otg: add OTG/dual-role core |
| Message-ID | <rIoJP-1vU-31@gated-at.bofh.it> |
| In reply to | #1418287 |
Hi Sergei, On 09/06/16 15:34, Sergei Shtylyov wrote: > On 6/9/2016 10:53 AM, Roger Quadros wrote: > >> It provides APIs for the following tasks >> >> - Registering an OTG/dual-role capable controller >> - Registering Host and Gadget controllers to OTG core >> - Providing inputs to and kicking the OTG state machine >> >> Provide a dual-role device (DRD) state machine. >> 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 'b_sess_vld'. >> >> Signed-off-by: Roger Quadros <rogerq@ti.com> > > [...] > >> diff --git a/drivers/usb/common/usb-otg.c b/drivers/usb/common/usb-otg.c >> new file mode 100644 >> index 0000000..baebe5c >> --- /dev/null >> +++ b/drivers/usb/common/usb-otg.c >> @@ -0,0 +1,833 @@ > [...] >> +/** >> + * Change USB protocol when there is a protocol change. >> + * fsm->lock must be held. >> + */ > > If you're using the kernel-doc comment, please follow the rules. > All your comments are valid and I'll fix the issues. <snip> > [...] > > Phew, that was a long patch... normally I don't review the patches that are such big. :-) Thanks for the patience and review :) -- cheers, -roger
[toc] | [prev] | [next] | [standalone]
| From | Roger Quadros <rogerq@ti.com> |
|---|---|
| Date | 2016-06-10 12:20 +0200 |
| Subject | Re: [PATCH v10 08/14] usb: otg: add OTG/dual-role core |
| Message-ID | <rIrHH-3il-17@gated-at.bofh.it> |
| In reply to | #1418287 |
Hi, On 09/06/16 15:34, Sergei Shtylyov wrote: > On 6/9/2016 10:53 AM, Roger Quadros wrote: > >> It provides APIs for the following tasks >> >> - Registering an OTG/dual-role capable controller >> - Registering Host and Gadget controllers to OTG core >> - Providing inputs to and kicking the OTG state machine >> >> Provide a dual-role device (DRD) state machine. >> 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 'b_sess_vld'. >> >> Signed-off-by: Roger Quadros <rogerq@ti.com> > <snip> > [...] >> +/** >> + * usb_otg_register_gadget - Register the gadget controller to OTG core >> + * @gadget: gadget controller > > We call that USB device controller (UDC). I'm not sure what you meant here... > And what about the 2nd arg, 'ops'? There are 2 data structures representing the Device controller. struct usb_gadget - represents a usb slave device struct usb_udc -struct usb_udc - describes one usb device controller usb_udc is for private use only. usb_otg_register_gadget() takes struct usb_gadget as argument. Do you want me to refer to struct usb_gadget as UDC? What is wrong with calling it gadget controller? -- cheers, -roger
[toc] | [prev] | [next] | [standalone]
Page 1 of 2 [1] 2 Next page →
Back to top | Article view | linux.kernel
csiph-web