2015-08-24 13:21:44

by Roger Quadros

[permalink] [raw]
Subject: [PATCH v4 00/13] USB: OTG/DRD Core functionality

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 platform related patches will be sent separately.

Series is based on Greg's usb-next tree.

Changelog:
---------
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.

Why?:
----

Most of the OTG drivers have been dealing with the OTG state machine
themselves and there is a scope for code re-use. This has been
partly addressed by the usb/common/usb-otg-fsm.c but it still
leaves the instantiation of the state machine and OTG timers
to the controller drivers. We re-use usb-otg-fsm.c but
go one step further by instantiating the state machine and timers
thus making it easier for drivers to implement OTG functionality.

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 in OTG mode. i.e. to stop and start them from a
central location. This central location should be the USB OTG 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 can't be done as of now and can be done from the OTG core.

What?:
-----

The OTG core instantiates the OTG/DRD Finite State Machine
per OTG controller and manages starting/stopping the
host and gadget controllers based on the bus state.

It provides APIs for the following

- Registering an OTG capable controller
struct otg_fsm *usb_otg_register(struct device *dev,
struct usb_otg_config *config);

int usb_otg_unregister(struct device *dev);

- Registering Host controllers to OTG core (used by hcd-core)
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);


- Registering Gadget controllers to OTG core (used by udc-core)
int usb_otg_register_gadget(struct usb_gadget *gadget,
struct otg_gadget_ops *ops);
int usb_otg_unregister_gadget(struct usb_gadget *gadget);


- Providing inputs to and kicking the OTG state machine
void usb_otg_sync_inputs(struct otg_fsm *fsm);
int usb_otg_kick_fsm(struct device *hcd_gcd_device);

- Getting controller device structure from OTG state machine instance
struct device *usb_otg_fsm_to_dev(struct otg_fsm *fsm);

'struct otg_fsm' is the interface to the OTG state machine.
It contains inputs to the fsm, status of the fsm and operations
for the OTG controller driver.

- Helper APIs for starting/stopping host/gadget controllers
int usb_otg_start_host(struct otg_fsm *fsm, int on);
int usb_otg_start_gadget(struct otg_fsm *fsm, int on);

Usage model:
-----------

- The OTG core needs to know what host and gadget controllers are
linked to the OTG controller. For DT boots we can provide that
information by adding "otg-controller" property to the host and
gadget controller nodes that points to the right otg controller.
For legacy boot we assume that OTG controller is the parent
of the host and gadget controllers. For DT if "otg-controller"
property is not present then parent child relationship constraint
applies.

- The OTG controller driver must call usb_otg_register() to register
itself with the OTG core. It must also provide the required
OTG configuration, fsm operations and timer timeouts (optional)
via struct usb_otg_config. The fsm operations will be called
depending on the OTG bus state.

- The host/gadget core stacks are modified to inform the OTG core
whenever a new host/gadget device is added. The OTG core then
checks if the host/gadget is part of the OTG controller and if yes
then prevents the host/gadget from starting till both host and
gadget are registered, OTG state machine is running and the
USB bus state is appropriate to start host/gadget.
For this, APIs have been added to host/gadget stacks to start/stop
the controllers from the OTG core.
For DT boots, If the OTG controller hasn't yet been registered
while the host/gadget are added, the OTG core will hold it in a wait list
and register them when the OTG controller registers.

- No modification is needed for the host/gadget controller drivers.
They must ensure that their start/stop methods can be called repeatedly
and any shared resources between host & gadget are properly managed.
The OTG core ensures that both are not started simultaneously.

- The OTG core instantiates one OTG state machine per OTG controller
and the necessary OTG timers to manage OTG state timeouts.
If none of the otg features are set during usb_otg_register() then it
instanciates a DRD (dual-role device) state machine instead.
The state machine is started when both host & gadget register and
stopped when either of them unregisters. The controllers are started
and stopped depending on bus state.

- During the lifetime of the OTG state machine, inputs can be
provided to it by modifying the appropriate members of 'struct otg_fsm'
and calling usb_otg_sync_inputs(). This is typically done by the
OTG controller driver that called usb_otg_register().

--
cheers,
-roger

Roger Quadros (13):
usb: otg-fsm: Add documentation for struct otg_fsm
usb: otg-fsm: support multiple instances
usb: otg-fsm: Prevent build warning "VDBG" redefined
otg-fsm: move usb_bus_start_enum into otg-fsm->ops
usb: hcd.h: Add OTG to HCD interface
usb: gadget.h: Add OTG to gadget interface
usb: otg: add OTG core
usb: doc: dt-binding: Add otg-controller property
usb: chipidea: move from CONFIG_USB_OTG_FSM to CONFIG_USB_OTG
usb: hcd: Adapt to OTG core
usb: core: hub: Notify OTG fsm when A device sets b_hnp_enable
usb: gadget: udc: adapt to OTG core
usb: otg: Add dual-role device (DRD) support

Documentation/devicetree/bindings/usb/generic.txt | 5 +
Documentation/usb/chipidea.txt | 2 +-
MAINTAINERS | 4 +-
drivers/usb/Kconfig | 2 +-
drivers/usb/Makefile | 1 +
drivers/usb/chipidea/Makefile | 2 +-
drivers/usb/chipidea/ci.h | 2 +-
drivers/usb/chipidea/otg_fsm.c | 1 +
drivers/usb/chipidea/otg_fsm.h | 2 +-
drivers/usb/common/Makefile | 3 +-
drivers/usb/common/usb-otg-fsm.c | 26 +-
drivers/usb/common/usb-otg.c | 1223 +++++++++++++++++++++
drivers/usb/common/usb-otg.h | 71 ++
drivers/usb/core/Kconfig | 11 +-
drivers/usb/core/hcd.c | 55 +-
drivers/usb/core/hub.c | 10 +-
drivers/usb/gadget/udc/udc-core.c | 124 ++-
drivers/usb/phy/Kconfig | 2 +-
drivers/usb/phy/phy-fsl-usb.c | 3 +
include/linux/usb/gadget.h | 14 +
include/linux/usb/hcd.h | 14 +
include/linux/usb/otg-fsm.h | 116 +-
include/linux/usb/otg.h | 191 +++-
23 files changed, 1808 insertions(+), 76 deletions(-)
create mode 100644 drivers/usb/common/usb-otg.c
create mode 100644 drivers/usb/common/usb-otg.h

--
2.1.4


2015-08-24 13:21:47

by Roger Quadros

[permalink] [raw]
Subject: [PATCH v4 01/13] usb: otg-fsm: Add documentation for struct otg_fsm

struct otg_fsm is the interface to the OTG state machine.

Document the input, output and internal state variables.
Definations are taken from Table 7-2 and Table 7-4 of
the USB OTG & EH Specification Rev.2.0

Re-arrange some of the members as per use case for more
clarity.

Signed-off-by: Roger Quadros <[email protected]>
Acked-by: Peter Chen <[email protected]>
---
include/linux/usb/otg-fsm.h | 90 +++++++++++++++++++++++++++++++++++++++++----
1 file changed, 83 insertions(+), 7 deletions(-)

diff --git a/include/linux/usb/otg-fsm.h b/include/linux/usb/otg-fsm.h
index f728f18..fc5b4d9 100644
--- a/include/linux/usb/otg-fsm.h
+++ b/include/linux/usb/otg-fsm.h
@@ -59,37 +59,113 @@ enum otg_fsm_timer {
NUM_OTG_FSM_TIMERS,
};

-/* OTG state machine according to the OTG spec */
+/**
+ * struct otg_fsm - OTG state machine according to the OTG spec
+ *
+ * OTG hardware Inputs
+ *
+ * Common inputs for A and B device
+ * @id: TRUE for B-device, FALSE for A-device.
+ * @adp_change: TRUE when current ADP measurement (n) value, compared to the
+ * ADP measurement taken at n-2, differs by more than CADP_THR
+ * @power_up: TRUE when the OTG device first powers up its USB system and
+ * ADP measurement taken if ADP capable
+ *
+ * A-Device state inputs
+ * @a_srp_det: TRUE if the A-device detects SRP
+ * @a_vbus_vld: TRUE when VBUS voltage is in regulation
+ * @b_conn: TRUE if the A-device detects connection from the B-device
+ * @a_bus_resume: TRUE when the B-device detects that the A-device is signaling
+ * a resume (K state)
+ * B-Device state inputs
+ * @a_bus_suspend: TRUE when the B-device detects that the A-device has put the
+ * bus into suspend
+ * @a_conn: TRUE if the B-device detects a connection from the A-device
+ * @b_se0_srp: TRUE when the line has been at SE0 for more than the minimum
+ * time before generating SRP
+ * @b_ssend_srp: TRUE when the VBUS has been below VOTG_SESS_VLD for more than
+ * the minimum time before generating SRP
+ * @b_sess_vld: TRUE when the B-device detects that the voltage on VBUS is
+ * above VOTG_SESS_VLD
+ * @test_device: TRUE when the B-device switches to B-Host and detects an OTG
+ * test device. This must be set by host/hub driver
+ *
+ * Application inputs (A-Device)
+ * @a_bus_drop: TRUE when A-device application needs to power down the bus
+ * @a_bus_req: TRUE when A-device application wants to use the bus.
+ * FALSE to suspend the bus
+ *
+ * Application inputs (B-Device)
+ * @b_bus_req: TRUE during the time that the Application running on the
+ * B-device wants to use the bus
+ *
+ * Auxilary inputs (OTG v1.3 only. Obsolete now.)
+ * @a_sess_vld: TRUE if the A-device detects that VBUS is above VA_SESS_VLD
+ * @b_bus_suspend: TRUE when the A-device detects that the B-device has put
+ * the bus into suspend
+ * @b_bus_resume: TRUE when the A-device detects that the B-device is signaling
+ * resume on the bus
+ *
+ * OTG Output status. Read only for users. Updated by OTG FSM helpers defined
+ * in this file
+ *
+ * Outputs for Both A and B device
+ * @drv_vbus: TRUE when A-device is driving VBUS
+ * @loc_conn: TRUE when the local device has signaled that it is connected
+ * to the bus
+ * @loc_sof: TRUE when the local device is generating activity on the bus
+ * @adp_prb: TRUE when the local device is in the process of doing
+ * ADP probing
+ *
+ * Outputs for B-device state
+ * @adp_sns: TRUE when the B-device is in the process of carrying out
+ * ADP sensing
+ * @data_pulse: TRUE when the B-device is performing data line pulsing
+ *
+ * Internal Variables
+ *
+ * a_set_b_hnp_en: TRUE when the A-device has successfully set the
+ * b_hnp_enable bit in the B-device.
+ * Unused as OTG fsm uses otg->host->b_hnp_enable instead
+ * b_srp_done: TRUE when the B-device has completed initiating SRP
+ * b_hnp_enable: TRUE when the B-device has accepted the
+ * SetFeature(b_hnp_enable) B-device.
+ * Unused as OTG fsm uses otg->gadget->b_hnp_enable instead
+ * 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
+ */
struct otg_fsm {
/* Input */
int id;
int adp_change;
int power_up;
- int test_device;
- int a_bus_drop;
- int a_bus_req;
int a_srp_det;
int a_vbus_vld;
int b_conn;
int a_bus_resume;
int a_bus_suspend;
int a_conn;
- int b_bus_req;
int b_se0_srp;
int b_ssend_srp;
int b_sess_vld;
+ int test_device;
+ int a_bus_drop;
+ int a_bus_req;
+ int b_bus_req;
+
/* Auxilary inputs */
int a_sess_vld;
int b_bus_resume;
int b_bus_suspend;

/* Output */
- int data_pulse;
int drv_vbus;
int loc_conn;
int loc_sof;
int adp_prb;
int adp_sns;
+ int data_pulse;

/* Internal variables */
int a_set_b_hnp_en;
@@ -97,7 +173,7 @@ struct otg_fsm {
int b_hnp_enable;
int a_clr_err;

- /* Informative variables */
+ /* Informative variables. All unused as of now */
int a_bus_drop_inf;
int a_bus_req_inf;
int a_clr_err_inf;
--
2.1.4

2015-08-24 13:21:49

by Roger Quadros

[permalink] [raw]
Subject: [PATCH v4 02/13] usb: otg-fsm: support multiple instances

Move the state_changed variable into struct otg_fsm
so that we can support multiple instances.

Signed-off-by: Roger Quadros <[email protected]>
---
drivers/usb/common/usb-otg-fsm.c | 10 ++++------
include/linux/usb/otg-fsm.h | 1 +
2 files changed, 5 insertions(+), 6 deletions(-)

diff --git a/drivers/usb/common/usb-otg-fsm.c b/drivers/usb/common/usb-otg-fsm.c
index 61d538a..32862a4 100644
--- a/drivers/usb/common/usb-otg-fsm.c
+++ b/drivers/usb/common/usb-otg-fsm.c
@@ -61,8 +61,6 @@ static int otg_set_protocol(struct otg_fsm *fsm, int protocol)
return 0;
}

-static int state_changed;
-
/* Called when leaving a state. Do state clean up jobs here */
static void otg_leave_state(struct otg_fsm *fsm, enum usb_otg_state old_state)
{
@@ -123,7 +121,6 @@ static void otg_leave_state(struct otg_fsm *fsm, enum usb_otg_state old_state)
/* Called when entering a state */
static int otg_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state)
{
- state_changed = 1;
if (fsm->otg->state == new_state)
return 0;
VDBG("Set state: %s\n", usb_otg_state_string(new_state));
@@ -237,6 +234,7 @@ static int otg_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state)
}

fsm->otg->state = new_state;
+ fsm->state_changed = 1;
return 0;
}

@@ -248,7 +246,7 @@ int otg_statemachine(struct otg_fsm *fsm)
mutex_lock(&fsm->lock);

state = fsm->otg->state;
- state_changed = 0;
+ fsm->state_changed = 0;
/* State machine state change judgement */

switch (state) {
@@ -361,7 +359,7 @@ int otg_statemachine(struct otg_fsm *fsm)
}
mutex_unlock(&fsm->lock);

- VDBG("quit statemachine, changed = %d\n", state_changed);
- return state_changed;
+ VDBG("quit statemachine, changed = %d\n", fsm->state_changed);
+ return fsm->state_changed;
}
EXPORT_SYMBOL_GPL(otg_statemachine);
diff --git a/include/linux/usb/otg-fsm.h b/include/linux/usb/otg-fsm.h
index fc5b4d9..20c8219 100644
--- a/include/linux/usb/otg-fsm.h
+++ b/include/linux/usb/otg-fsm.h
@@ -195,6 +195,7 @@ struct otg_fsm {
/* Current usb protocol used: 0:undefine; 1:host; 2:client */
int protocol;
struct mutex lock;
+ bool state_changed;
};

struct otg_fsm_ops {
--
2.1.4

2015-08-24 13:25:43

by Roger Quadros

[permalink] [raw]
Subject: [PATCH v4 03/13] usb: otg-fsm: Prevent build warning "VDBG" redefined

If usb/otg-fsm.h and usb/composite.h are included together
then it results in the build warning [1].

Prevent that by using dev_vdbg() instead.

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 <[email protected]>
Acked-by: Peter Chen <[email protected]>
---
drivers/usb/chipidea/otg_fsm.c | 1 +
drivers/usb/common/usb-otg-fsm.c | 12 +++++++-----
drivers/usb/phy/phy-fsl-usb.c | 1 +
include/linux/usb/otg-fsm.h | 19 ++++---------------
4 files changed, 13 insertions(+), 20 deletions(-)

diff --git a/drivers/usb/chipidea/otg_fsm.c b/drivers/usb/chipidea/otg_fsm.c
index 00ab59d..f644752 100644
--- a/drivers/usb/chipidea/otg_fsm.c
+++ b/drivers/usb/chipidea/otg_fsm.c
@@ -776,6 +776,7 @@ int ci_hdrc_otg_fsm_init(struct ci_hdrc *ci)
ci->fsm.id = hw_read_otgsc(ci, OTGSC_ID) ? 1 : 0;
ci->fsm.otg->state = OTG_STATE_UNDEFINED;
ci->fsm.ops = &ci_otg_ops;
+ ci->fsm.dev = ci->dev;

mutex_init(&ci->fsm.lock);

diff --git a/drivers/usb/common/usb-otg-fsm.c b/drivers/usb/common/usb-otg-fsm.c
index 32862a4..a46f29a 100644
--- a/drivers/usb/common/usb-otg-fsm.c
+++ b/drivers/usb/common/usb-otg-fsm.c
@@ -36,8 +36,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(fsm->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(fsm, 0);
@@ -123,7 +124,7 @@ static int otg_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state)
{
if (fsm->otg->state == new_state)
return 0;
- VDBG("Set state: %s\n", usb_otg_state_string(new_state));
+ dev_vdbg(fsm->dev, "Set state: %s\n", usb_otg_state_string(new_state));
otg_leave_state(fsm, fsm->otg->state);
switch (new_state) {
case OTG_STATE_B_IDLE:
@@ -251,7 +252,7 @@ int otg_statemachine(struct otg_fsm *fsm)

switch (state) {
case OTG_STATE_UNDEFINED:
- VDBG("fsm->id = %d\n", fsm->id);
+ dev_vdbg(fsm->dev, "fsm->id = %d\n", fsm->id);
if (fsm->id)
otg_set_state(fsm, OTG_STATE_B_IDLE);
else
@@ -359,7 +360,8 @@ int otg_statemachine(struct otg_fsm *fsm)
}
mutex_unlock(&fsm->lock);

- VDBG("quit statemachine, changed = %d\n", fsm->state_changed);
+ dev_vdbg(fsm->dev, "quit statemachine, changed = %d\n",
+ fsm->state_changed);
return fsm->state_changed;
}
EXPORT_SYMBOL_GPL(otg_statemachine);
diff --git a/drivers/usb/phy/phy-fsl-usb.c b/drivers/usb/phy/phy-fsl-usb.c
index 94eb292..ee3f2c2 100644
--- a/drivers/usb/phy/phy-fsl-usb.c
+++ b/drivers/usb/phy/phy-fsl-usb.c
@@ -817,6 +817,7 @@ static int fsl_otg_conf(struct platform_device *pdev)

/* Set OTG state machine operations */
fsl_otg_tc->fsm.ops = &fsl_otg_ops;
+ fsl_otg_tc->fsm.dev = &pdev->dev;

/* initialize the otg structure */
fsl_otg_tc->phy.label = DRIVER_DESC;
diff --git a/include/linux/usb/otg-fsm.h b/include/linux/usb/otg-fsm.h
index 20c8219..672551c 100644
--- a/include/linux/usb/otg-fsm.h
+++ b/include/linux/usb/otg-fsm.h
@@ -18,24 +18,10 @@
#ifndef __LINUX_USB_OTG_FSM_H
#define __LINUX_USB_OTG_FSM_H

+#include <linux/device.h>
#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)
@@ -196,6 +182,9 @@ struct otg_fsm {
int protocol;
struct mutex lock;
bool state_changed;
+
+ /* for debug prints */
+ struct device *dev;
};

struct otg_fsm_ops {
--
2.1.4

2015-08-24 13:21:55

by Roger Quadros

[permalink] [raw]
Subject: [PATCH v4 04/13] otg-fsm: move usb_bus_start_enum into otg-fsm->ops

This is to prevent missing symbol build error if OTG is
enabled (built-in) and HCD core (CONFIG_USB) is module.

Signed-off-by: Roger Quadros <[email protected]>
Acked-by: Peter Chen <[email protected]>
---
drivers/usb/common/usb-otg-fsm.c | 6 ++++--
drivers/usb/phy/phy-fsl-usb.c | 2 ++
include/linux/usb/otg-fsm.h | 1 +
3 files changed, 7 insertions(+), 2 deletions(-)

diff --git a/drivers/usb/common/usb-otg-fsm.c b/drivers/usb/common/usb-otg-fsm.c
index a46f29a..6e56c8c 100644
--- a/drivers/usb/common/usb-otg-fsm.c
+++ b/drivers/usb/common/usb-otg-fsm.c
@@ -165,8 +165,10 @@ static int otg_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state)
otg_loc_conn(fsm, 0);
otg_loc_sof(fsm, 1);
otg_set_protocol(fsm, PROTO_HOST);
- usb_bus_start_enum(fsm->otg->host,
- fsm->otg->host->otg_port);
+ if (fsm->ops->start_enum) {
+ fsm->ops->start_enum(fsm->otg->host,
+ fsm->otg->host->otg_port);
+ }
break;
case OTG_STATE_A_IDLE:
otg_drv_vbus(fsm, 0);
diff --git a/drivers/usb/phy/phy-fsl-usb.c b/drivers/usb/phy/phy-fsl-usb.c
index ee3f2c2..19541ed 100644
--- a/drivers/usb/phy/phy-fsl-usb.c
+++ b/drivers/usb/phy/phy-fsl-usb.c
@@ -783,6 +783,8 @@ static struct otg_fsm_ops fsl_otg_ops = {

.start_host = fsl_otg_start_host,
.start_gadget = fsl_otg_start_gadget,
+
+ .start_enum = usb_bus_start_enum,
};

/* Initialize the global variable fsl_otg_dev and request IRQ for OTG */
diff --git a/include/linux/usb/otg-fsm.h b/include/linux/usb/otg-fsm.h
index 672551c..75e82cc 100644
--- a/include/linux/usb/otg-fsm.h
+++ b/include/linux/usb/otg-fsm.h
@@ -199,6 +199,7 @@ struct otg_fsm_ops {
void (*del_timer)(struct otg_fsm *fsm, enum otg_fsm_timer timer);
int (*start_host)(struct otg_fsm *fsm, int on);
int (*start_gadget)(struct otg_fsm *fsm, int on);
+ int (*start_enum)(struct usb_bus *bus, unsigned port_num);
};


--
2.1.4

2015-08-24 13:21:58

by Roger Quadros

[permalink] [raw]
Subject: [PATCH v4 05/13] usb: hcd.h: Add OTG to HCD interface

The OTG core will use struct otg_hcd_ops to
add/remove the HCD controller.

The main purpose of this interface is to avoid directly
calling usb_add/remove_hcd() from the OTG core as they
wouldn't be defined in the built-in symbol table if
CONFIG_USB is m.

Signed-off-by: Roger Quadros <[email protected]>
Reviewed-by: Peter Chen <[email protected]>
---
include/linux/usb/hcd.h | 14 ++++++++++++++
1 file changed, 14 insertions(+)

diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h
index d2784c1..27d78b1 100644
--- a/include/linux/usb/hcd.h
+++ b/include/linux/usb/hcd.h
@@ -386,6 +386,20 @@ struct hc_driver {

};

+/**
+ * struct otg_hcd_ops - Interface between OTG core and HCD
+ *
+ * Provided by the HCD core to allow the OTG core to start/stop the HCD
+ *
+ * @add: function to add the HCD
+ * @remove: function to remove the HCD
+ */
+struct otg_hcd_ops {
+ int (*add)(struct usb_hcd *hcd,
+ unsigned int irqnum, unsigned long irqflags);
+ void (*remove)(struct usb_hcd *hcd);
+};
+
static inline int hcd_giveback_urb_in_bh(struct usb_hcd *hcd)
{
return hcd->driver->flags & HCD_BH;
--
2.1.4

2015-08-24 13:22:03

by Roger Quadros

[permalink] [raw]
Subject: [PATCH v4 06/13] usb: gadget.h: Add OTG to gadget interface

The OTG core will use struct otg_gadget_ops to
start/stop the gadget controller.

The main purpose of this interface is to avoid directly
calling usb_gadget_start/stop() from the OTG core as they
wouldn't be defined in the built-in symbol table if
CONFIG_USB_GADGET is m.

Signed-off-by: Roger Quadros <[email protected]>
Reviewed-by: Peter Chen <[email protected]>
---
include/linux/usb/gadget.h | 14 ++++++++++++++
1 file changed, 14 insertions(+)

diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h
index c14a69b..c019242 100644
--- a/include/linux/usb/gadget.h
+++ b/include/linux/usb/gadget.h
@@ -1052,6 +1052,20 @@ struct usb_gadget_driver {
};


+/*-------------------------------------------------------------------------*/
+
+/**
+ * struct otg_gadget_ops - Interface between OTG core and gadget
+ *
+ * Provided by the gadget core to allow the OTG core to start/stop the gadget
+ *
+ * @start: function to start the gadget
+ * @stop: function to stop the gadget
+ */
+struct otg_gadget_ops {
+ int (*start)(struct usb_gadget *gadget);
+ int (*stop)(struct usb_gadget *gadget);
+};

/*-------------------------------------------------------------------------*/

--
2.1.4

2015-08-24 13:22:11

by Roger Quadros

[permalink] [raw]
Subject: [PATCH v4 07/13] usb: otg: add OTG core

The OTG core instantiates the OTG Finite State Machine
per OTG controller and manages starting/stopping the
host and gadget controllers based on the bus state.

It provides APIs for the following tasks

- Registering an OTG capable controller
- Registering Host and Gadget controllers to OTG core
- Providing inputs to and kicking the OTG state machine

Signed-off-by: Roger Quadros <[email protected]>
---
MAINTAINERS | 4 +-
drivers/usb/Kconfig | 2 +-
drivers/usb/Makefile | 1 +
drivers/usb/common/Makefile | 3 +-
drivers/usb/common/usb-otg.c | 1061 ++++++++++++++++++++++++++++++++++++++++++
drivers/usb/common/usb-otg.h | 71 +++
drivers/usb/core/Kconfig | 11 +-
include/linux/usb/otg.h | 189 +++++++-
8 files changed, 1321 insertions(+), 21 deletions(-)
create mode 100644 drivers/usb/common/usb-otg.c
create mode 100644 drivers/usb/common/usb-otg.h

diff --git a/MAINTAINERS b/MAINTAINERS
index a9ae6c1..4bc1279 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -10667,12 +10667,14 @@ S: Maintained
F: Documentation/usb/ohci.txt
F: drivers/usb/host/ohci*

-USB OTG FSM (Finite State Machine)
+USB OTG/DRD core and FSM (Finite State Machine)
M: Peter Chen <[email protected]>
+M: Roger Quadros <[email protected]>
T: git git://git.kernel.org/pub/scm/linux/kernel/git/peter.chen/usb.git
L: [email protected]
S: Maintained
F: drivers/usb/common/usb-otg-fsm.c
+F: drivers/usb/common/usb-otg.c

USB OVER IP DRIVER
M: Valentina Manea <[email protected]>
diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index 8ed451d..5b625e2 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -32,7 +32,7 @@ if USB_SUPPORT
config USB_COMMON
tristate
default y
- depends on USB || USB_GADGET
+ depends on USB || USB_GADGET || USB_OTG

config USB_ARCH_HAS_HCD
def_bool y
diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
index d8926c6..769d13b 100644
--- a/drivers/usb/Makefile
+++ b/drivers/usb/Makefile
@@ -60,5 +60,6 @@ obj-$(CONFIG_USB_RENESAS_USBHS) += renesas_usbhs/
obj-$(CONFIG_USB_GADGET) += gadget/

obj-$(CONFIG_USB_COMMON) += common/
+obj-$(CONFIG_USB_OTG) += common/

obj-$(CONFIG_USBIP_CORE) += usbip/
diff --git a/drivers/usb/common/Makefile b/drivers/usb/common/Makefile
index 6bbb3ec..730d928 100644
--- a/drivers/usb/common/Makefile
+++ b/drivers/usb/common/Makefile
@@ -6,5 +6,6 @@ obj-$(CONFIG_USB_COMMON) += usb-common.o
usb-common-y += common.o
usb-common-$(CONFIG_USB_LED_TRIG) += led.o

-obj-$(CONFIG_USB_OTG_FSM) += usb-otg-fsm.o
obj-$(CONFIG_USB_ULPI_BUS) += ulpi.o
+usbotg-y := usb-otg.o usb-otg-fsm.o
+obj-$(CONFIG_USB_OTG) += usbotg.o
diff --git a/drivers/usb/common/usb-otg.c b/drivers/usb/common/usb-otg.c
new file mode 100644
index 0000000..930c2fe
--- /dev/null
+++ b/drivers/usb/common/usb-otg.c
@@ -0,0 +1,1061 @@
+/**
+ * drivers/usb/common/usb-otg.c - USB OTG core
+ *
+ * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com
+ * Author: Roger Quadros <[email protected]>
+ *
+ * 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/ktime.h>
+#include <linux/hrtimer.h>
+#include <linux/list.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/usb/otg.h>
+#include <linux/usb/phy.h> /* enum usb_otg_state */
+#include <linux/usb/gadget.h>
+#include <linux/workqueue.h>
+
+#include "usb-otg.h"
+
+struct otg_gcd {
+ struct usb_gadget *gadget;
+ struct otg_gadget_ops *ops;
+};
+
+/* OTG device list */
+LIST_HEAD(otg_list);
+static DEFINE_MUTEX(otg_list_mutex);
+
+/* Hosts and Gadgets waiting for OTG controller */
+struct otg_wait_data {
+ struct device *dev; /* OTG controller device */
+
+ struct otg_hcd primary_hcd;
+ struct otg_hcd shared_hcd;
+ struct otg_gcd gcd;
+ struct list_head list;
+};
+
+LIST_HEAD(wait_list);
+static DEFINE_MUTEX(wait_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 wait list and return
+ * otg_wait_data, else NULL.
+ *
+ * wait_list_mutex must be held.
+ */
+static struct otg_wait_data *usb_otg_get_wait(struct device *otg_dev)
+{
+ struct otg_wait_data *wait;
+
+ if (!otg_dev)
+ return NULL;
+
+ /* is there an entry for this otg_dev ?*/
+ list_for_each_entry(wait, &wait_list, list) {
+ if (wait->dev == otg_dev)
+ return wait;
+ }
+
+ return NULL;
+}
+
+/**
+ * Add the hcd to our wait list
+ */
+static int usb_otg_hcd_wait_add(struct device *otg_dev, struct usb_hcd *hcd,
+ unsigned int irqnum, unsigned long irqflags,
+ struct otg_hcd_ops *ops)
+{
+ struct otg_wait_data *wait;
+ int ret = -EINVAL;
+
+ mutex_lock(&wait_list_mutex);
+
+ wait = usb_otg_get_wait(otg_dev);
+ if (!wait) {
+ /* Not yet in wait list? allocate and add */
+ wait = kzalloc(sizeof(*wait), GFP_KERNEL);
+ if (!wait) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ wait->dev = otg_dev;
+ list_add_tail(&wait->list, &wait_list);
+ }
+
+ if (usb_otg_hcd_is_primary_hcd(hcd)) {
+ if (wait->primary_hcd.hcd) /* already assigned? */
+ goto fail;
+
+ wait->primary_hcd.hcd = hcd;
+ wait->primary_hcd.irqnum = irqnum;
+ wait->primary_hcd.irqflags = irqflags;
+ wait->primary_hcd.ops = ops;
+ } else {
+ if (wait->shared_hcd.hcd) /* already assigned? */
+ goto fail;
+
+ wait->shared_hcd.hcd = hcd;
+ wait->shared_hcd.irqnum = irqnum;
+ wait->shared_hcd.irqflags = irqflags;
+ wait->shared_hcd.ops = ops;
+ }
+
+ mutex_unlock(&wait_list_mutex);
+ return 0;
+
+fail:
+ mutex_unlock(&wait_list_mutex);
+ return ret;
+}
+
+/**
+ * Check and free wait list entry if empty
+ *
+ * wait_list_mutex must be held
+ */
+static void usb_otg_check_free_wait(struct otg_wait_data *wait)
+{
+ if (wait->primary_hcd.hcd || wait->shared_hcd.hcd || wait->gcd.gadget)
+ return;
+
+ list_del(&wait->list);
+ kfree(wait);
+}
+
+/**
+ * Remove the hcd from our wait list
+ */
+static int usb_otg_hcd_wait_remove(struct usb_hcd *hcd)
+{
+ struct otg_wait_data *wait;
+
+ mutex_lock(&wait_list_mutex);
+
+ /* is there an entry for this hcd ?*/
+ list_for_each_entry(wait, &wait_list, list) {
+ if (wait->primary_hcd.hcd == hcd) {
+ wait->primary_hcd.hcd = 0;
+ goto found;
+ } else if (wait->shared_hcd.hcd == hcd) {
+ wait->shared_hcd.hcd = 0;
+ goto found;
+ }
+ }
+
+ mutex_unlock(&wait_list_mutex);
+ return -EINVAL;
+
+found:
+ usb_otg_check_free_wait(wait);
+ mutex_unlock(&wait_list_mutex);
+
+ return 0;
+}
+
+/**
+ * Add the gadget to our wait list
+ */
+static int usb_otg_gadget_wait_add(struct device *otg_dev,
+ struct usb_gadget *gadget,
+ struct otg_gadget_ops *ops)
+{
+ struct otg_wait_data *wait;
+ int ret = -EINVAL;
+
+ mutex_lock(&wait_list_mutex);
+
+ wait = usb_otg_get_wait(otg_dev);
+ if (!wait) {
+ /* Not yet in wait list? allocate and add */
+ wait = kzalloc(sizeof(*wait), GFP_KERNEL);
+ if (!wait) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ wait->dev = otg_dev;
+ list_add_tail(&wait->list, &wait_list);
+ }
+
+ if (wait->gcd.gadget) /* already assigned? */
+ goto fail;
+
+ wait->gcd.gadget = gadget;
+ wait->gcd.ops = ops;
+ mutex_unlock(&wait_list_mutex);
+
+ return 0;
+
+fail:
+ mutex_unlock(&wait_list_mutex);
+ return ret;
+}
+
+/**
+ * Remove the gadget from our wait list
+ */
+static int usb_otg_gadget_wait_remove(struct usb_gadget *gadget)
+{
+ struct otg_wait_data *wait;
+
+ mutex_lock(&wait_list_mutex);
+
+ /* is there an entry for this gadget ?*/
+ list_for_each_entry(wait, &wait_list, list) {
+ if (wait->gcd.gadget == gadget) {
+ wait->gcd.gadget = 0;
+ goto found;
+ }
+ }
+
+ mutex_unlock(&wait_list_mutex);
+
+ return -EINVAL;
+
+found:
+ usb_otg_check_free_wait(wait);
+ mutex_unlock(&wait_list_mutex);
+
+ return 0;
+}
+
+/**
+ * Register pending host/gadget and remove entry from wait list
+ */
+static void usb_otg_flush_wait(struct device *otg_dev)
+{
+ struct otg_wait_data *wait;
+ struct otg_hcd *host;
+ struct otg_gcd *gadget;
+
+ mutex_lock(&wait_list_mutex);
+
+ wait = usb_otg_get_wait(otg_dev);
+ if (!wait)
+ goto done;
+
+ dev_dbg(otg_dev, "otg: registering pending host/gadget\n");
+ gadget = &wait->gcd;
+ if (gadget)
+ usb_otg_register_gadget(gadget->gadget, gadget->ops);
+
+ host = &wait->primary_hcd;
+ if (host->hcd)
+ usb_otg_register_hcd(host->hcd, host->irqnum, host->irqflags,
+ host->ops);
+
+ host = &wait->shared_hcd;
+ if (host->hcd)
+ usb_otg_register_hcd(host->hcd, host->irqnum, host->irqflags,
+ host->ops);
+
+ list_del(&wait->list);
+ kfree(wait);
+
+done:
+ mutex_unlock(&wait_list_mutex);
+}
+
+/**
+ * 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 *otgd;
+
+ if (!otg_dev)
+ return NULL;
+
+ list_for_each_entry(otgd, &otg_list, list) {
+ if (otgd->dev == otg_dev)
+ return otgd;
+ }
+
+ return NULL;
+}
+
+/**
+ * Get OTG device from host or gadget device.
+ *
+ * For non device tree boot, the OTG controller is assumed to be
+ * the parent of the host/gadget device.
+ * For device tree boot, the OTG controller is derived from the
+ * "otg-controller" property.
+ */
+static struct device *usb_otg_get_device(struct device *hcd_gcd_dev)
+{
+ struct device *otg_dev;
+
+ if (!hcd_gcd_dev)
+ return NULL;
+
+ if (hcd_gcd_dev->of_node) {
+ struct device_node *np;
+ struct platform_device *pdev;
+
+ np = of_parse_phandle(hcd_gcd_dev->of_node, "otg-controller",
+ 0);
+ if (!np)
+ goto legacy; /* continue legacy way */
+
+ pdev = of_find_device_by_node(np);
+ of_node_put(np);
+ if (!pdev) {
+ dev_err(&pdev->dev, "couldn't get otg-controller device\n");
+ return NULL;
+ }
+
+ otg_dev = &pdev->dev;
+ return otg_dev;
+ }
+
+legacy:
+ /* otg device is parent and must be registered */
+ otg_dev = hcd_gcd_dev->parent;
+ if (!usb_otg_get_data(otg_dev))
+ return NULL;
+
+ return otg_dev;
+}
+
+/**
+ * timer callback to set timeout bit and kick FSM
+ */
+static enum hrtimer_restart set_tmout(struct hrtimer *data)
+{
+ struct otg_timer *otgtimer;
+
+ otgtimer = container_of(data, struct otg_timer, timer);
+ if (otgtimer->timeout_bit)
+ *otgtimer->timeout_bit = 1;
+
+ usb_otg_sync_inputs(&otgtimer->otgd->fsm);
+
+ return HRTIMER_NORESTART;
+}
+
+/**
+ * Initialize one OTG timer with callback, timeout and timeout bit
+ */
+static void otg_timer_init(enum otg_fsm_timer id, struct usb_otg *otgd,
+ enum hrtimer_restart (*callback)(struct hrtimer *),
+ unsigned long expires_ms,
+ int *timeout_bit)
+{
+ struct otg_timer *otgtimer = &otgd->timers[id];
+ struct hrtimer *timer = &otgtimer->timer;
+
+ otgtimer->timeout = ms_to_ktime(expires_ms);
+ hrtimer_init(timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ timer->function = callback;
+
+ otgtimer->timeout_bit = timeout_bit;
+ otgtimer->otgd = otgd;
+}
+
+/**
+ * Initialize standard OTG timers
+ */
+static void usb_otg_init_timers(struct usb_otg *otgd, unsigned *timeouts)
+{
+ struct otg_fsm *fsm = &otgd->fsm;
+ unsigned long tmouts[NUM_OTG_FSM_TIMERS];
+ int i;
+
+ /* set default timeouts */
+ tmouts[A_WAIT_VRISE] = TA_WAIT_VRISE;
+ tmouts[A_WAIT_VFALL] = TA_WAIT_VFALL;
+ tmouts[A_WAIT_BCON] = TA_WAIT_BCON;
+ tmouts[A_AIDL_BDIS] = TA_AIDL_BDIS;
+ tmouts[A_BIDL_ADIS] = TA_BIDL_ADIS;
+ tmouts[B_ASE0_BRST] = TB_ASE0_BRST;
+ tmouts[B_SE0_SRP] = TB_SE0_SRP;
+ tmouts[B_SRP_FAIL] = TB_SRP_FAIL;
+
+ /* set controller provided timeouts */
+ if (timeouts) {
+ for (i = 0; i < NUM_OTG_FSM_TIMERS; i++) {
+ if (timeouts[i])
+ tmouts[i] = timeouts[i];
+ }
+ }
+
+ otg_timer_init(A_WAIT_VRISE, otgd, set_tmout, TA_WAIT_VRISE,
+ &fsm->a_wait_vrise_tmout);
+ otg_timer_init(A_WAIT_VFALL, otgd, set_tmout, TA_WAIT_VFALL,
+ &fsm->a_wait_vfall_tmout);
+ otg_timer_init(A_WAIT_BCON, otgd, set_tmout, TA_WAIT_BCON,
+ &fsm->a_wait_bcon_tmout);
+ otg_timer_init(A_AIDL_BDIS, otgd, set_tmout, TA_AIDL_BDIS,
+ &fsm->a_aidl_bdis_tmout);
+ otg_timer_init(A_BIDL_ADIS, otgd, set_tmout, TA_BIDL_ADIS,
+ &fsm->a_bidl_adis_tmout);
+ otg_timer_init(B_ASE0_BRST, otgd, set_tmout, TB_ASE0_BRST,
+ &fsm->b_ase0_brst_tmout);
+
+ otg_timer_init(B_SE0_SRP, otgd, set_tmout, TB_SE0_SRP,
+ &fsm->b_se0_srp);
+ otg_timer_init(B_SRP_FAIL, otgd, set_tmout, TB_SRP_FAIL,
+ &fsm->b_srp_done);
+
+ /* FIXME: what about A_WAIT_ENUM? */
+}
+
+/**
+ * OTG FSM ops function to add timer
+ */
+static void usb_otg_add_timer(struct otg_fsm *fsm, enum otg_fsm_timer id)
+{
+ struct usb_otg *otgd = container_of(fsm, struct usb_otg, fsm);
+ struct otg_timer *otgtimer = &otgd->timers[id];
+ struct hrtimer *timer = &otgtimer->timer;
+
+ if (!otgd->fsm_running)
+ return;
+
+ /* if timer is already active, exit */
+ if (hrtimer_active(timer)) {
+ dev_err(otgd->dev, "otg: timer %d is already running\n", id);
+ return;
+ }
+
+ hrtimer_start(timer, otgtimer->timeout, HRTIMER_MODE_REL);
+}
+
+/**
+ * OTG FSM ops function to delete timer
+ */
+static void usb_otg_del_timer(struct otg_fsm *fsm, enum otg_fsm_timer id)
+{
+ struct usb_otg *otgd = container_of(fsm, struct usb_otg, fsm);
+ struct hrtimer *timer = &otgd->timers[id].timer;
+
+ hrtimer_cancel(timer);
+}
+
+/**
+ * Helper function to start/stop otg host. For use by otg controller.
+ */
+int usb_otg_start_host(struct otg_fsm *fsm, int on)
+{
+ struct usb_otg *otgd = container_of(fsm, struct usb_otg, fsm);
+ struct otg_hcd_ops *hcd_ops;
+
+ dev_dbg(otgd->dev, "otg: %s %d\n", __func__, on);
+ if (!fsm->otg->host) {
+ WARN_ONCE(1, "otg: fsm running without host\n");
+ return 0;
+ }
+
+ if (on) {
+ /* start host */
+ hcd_ops = otgd->primary_hcd.ops;
+ hcd_ops->add(otgd->primary_hcd.hcd, otgd->primary_hcd.irqnum,
+ otgd->primary_hcd.irqflags);
+ if (otgd->shared_hcd.hcd) {
+ hcd_ops = otgd->shared_hcd.ops;
+ hcd_ops->add(otgd->shared_hcd.hcd,
+ otgd->shared_hcd.irqnum,
+ otgd->shared_hcd.irqflags);
+ }
+ } else {
+ /* stop host */
+ if (otgd->shared_hcd.hcd) {
+ hcd_ops = otgd->shared_hcd.ops;
+ hcd_ops->remove(otgd->shared_hcd.hcd);
+ }
+ hcd_ops = otgd->primary_hcd.ops;
+ hcd_ops->remove(otgd->primary_hcd.hcd);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usb_otg_start_host);
+
+/**
+ * Helper function to start/stop otg gadget. For use by otg controller.
+ */
+int usb_otg_start_gadget(struct otg_fsm *fsm, int on)
+{
+ struct usb_otg *otgd = container_of(fsm, struct usb_otg, fsm);
+ struct usb_gadget *gadget = fsm->otg->gadget;
+
+ dev_dbg(otgd->dev, "otg: %s %d\n", __func__, on);
+ if (!gadget) {
+ WARN_ONCE(1, "otg: fsm running without gadget\n");
+ return 0;
+ }
+
+ if (on)
+ otgd->gadget_ops->start(fsm->otg->gadget);
+ else
+ otgd->gadget_ops->stop(fsm->otg->gadget);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usb_otg_start_gadget);
+
+/**
+ * OTG FSM work function
+ */
+static void usb_otg_work(struct work_struct *work)
+{
+ struct usb_otg *otgd = container_of(work, struct usb_otg, work);
+
+ otg_statemachine(&otgd->fsm);
+}
+
+/**
+ * usb_otg_register() - Register the OTG device to OTG core
+ * @dev: OTG controller device.
+ * @config: OTG configuration.
+ *
+ * Register the OTG controller device with the USB OTG core.
+ * The associated Host and Gadget controllers will be prevented from
+ * being started till both are available for use.
+ *
+ * For non device tree boots, the OTG controller device must be the
+ * parent node of the Host and Gadget controllers.
+ *
+ * For device tree case, the otg-controller property must be present
+ * in the Host and Gadget controller node and it must point to the
+ * same OTG controller node.
+ *
+ * Return: struct otg_fsm * if success, NULL if error.
+ */
+struct otg_fsm *usb_otg_register(struct device *dev,
+ struct usb_otg_config *config)
+{
+ struct usb_otg *otgd;
+ struct otg_wait_data *wait;
+ 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 */
+ otgd = kzalloc(sizeof(*otgd), GFP_KERNEL);
+ if (!otgd) {
+ ret = -ENOMEM;
+ goto unlock;
+ }
+
+ otgd->dev = dev;
+ otgd->caps = &config->otg_caps;
+ INIT_WORK(&otgd->work, usb_otg_work);
+ otgd->wq = create_singlethread_workqueue("usb_otg");
+ if (!otgd->wq) {
+ dev_err(dev, "otg: %s: can't create workqueue\n",
+ __func__);
+ ret = -ENOMEM;
+ goto err_wq;
+ }
+
+ usb_otg_init_timers(otgd, config->otg_timeouts);
+
+ /* create copy of original ops */
+ otgd->fsm_ops = *config->fsm_ops;
+ /* FIXME: we ignore caller's timer ops */
+ otgd->fsm_ops.add_timer = usb_otg_add_timer;
+ otgd->fsm_ops.del_timer = usb_otg_del_timer;
+ /* set otg ops */
+ otgd->fsm.ops = &otgd->fsm_ops;
+ otgd->fsm.otg = otgd;
+
+ mutex_init(&otgd->fsm.lock);
+
+ list_add_tail(&otgd->list, &otg_list);
+ mutex_unlock(&otg_list_mutex);
+
+ /* were we in wait list? */
+ mutex_lock(&wait_list_mutex);
+ wait = usb_otg_get_wait(dev);
+ mutex_unlock(&wait_list_mutex);
+ if (wait) {
+ /* register pending host/gadget and flush from list */
+ usb_otg_flush_wait(dev);
+ }
+
+ return &otgd->fsm;
+
+err_wq:
+ kfree(otgd);
+unlock:
+ mutex_unlock(&otg_list_mutex);
+ return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(usb_otg_register);
+
+/**
+ * usb_otg_unregister() - Unregister the OTG device from USB OTG core
+ * @dev: OTG controller device.
+ *
+ * Unregister OTG 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 *otgd;
+
+ mutex_lock(&otg_list_mutex);
+ otgd = usb_otg_get_data(dev);
+ if (!otgd) {
+ 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 (otgd->fsm.otg->host || otgd->fsm.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(otgd->wq);
+
+ /* remove from otg list */
+ list_del(&otgd->list);
+ kfree(otgd);
+ 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 otg_fsm *fsm)
+{
+ struct usb_otg *otgd = container_of(fsm, struct usb_otg, fsm);
+
+ if (otgd->fsm_running)
+ goto kick_fsm;
+
+ if (!fsm->otg->host) {
+ dev_info(otgd->dev, "otg: can't start till host registers\n");
+ return;
+ }
+
+ if (!fsm->otg->gadget) {
+ dev_info(otgd->dev, "otg: can't start till gadget registers\n");
+ return;
+ }
+
+ otgd->fsm_running = true;
+kick_fsm:
+ queue_work(otgd->wq, &otgd->work);
+}
+
+/**
+ * stop the OTG FSM. Stops Host & Gadget controllers as well.
+ * fsm->lock must be held
+ */
+static void usb_otg_stop_fsm(struct otg_fsm *fsm)
+{
+ struct usb_otg *otgd = container_of(fsm, struct usb_otg, fsm);
+ int i;
+
+ if (!otgd->fsm_running)
+ return;
+
+ /* no more new events queued */
+ otgd->fsm_running = false;
+
+ /* Stop state machine / timers */
+ for (i = 0; i < ARRAY_SIZE(otgd->timers); i++)
+ hrtimer_cancel(&otgd->timers[i].timer);
+
+ flush_workqueue(otgd->wq);
+ fsm->otg->state = OTG_STATE_UNDEFINED;
+
+ /* stop host/gadget immediately */
+ if (fsm->protocol == PROTO_HOST)
+ otg_start_host(fsm, 0);
+ else if (fsm->protocol == PROTO_GADGET)
+ otg_start_gadget(fsm, 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 otg_fsm *fsm)
+{
+ struct usb_otg *otgd = container_of(fsm, struct usb_otg, fsm);
+
+ /* Don't kick FSM till it has started */
+ if (!otgd->fsm_running)
+ return;
+
+ /* Kick FSM */
+ queue_work(otgd->wq, &otgd->work);
+}
+EXPORT_SYMBOL_GPL(usb_otg_sync_inputs);
+
+/**
+ * usb_otg_kick_fsm - Kick the OTG state machine
+ * @hcd_gcd_device: Host/Gadget 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 *hcd_gcd_device)
+{
+ struct usb_otg *otgd;
+
+ mutex_lock(&otg_list_mutex);
+ otgd = usb_otg_get_data(usb_otg_get_device(hcd_gcd_device));
+ mutex_unlock(&otg_list_mutex);
+ if (!otgd) {
+ dev_dbg(hcd_gcd_device, "otg: %s: invalid host/gadget device\n",
+ __func__);
+ return -ENODEV;
+ }
+
+ usb_otg_sync_inputs(&otgd->fsm);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usb_otg_kick_fsm);
+
+/**
+ * usb_otg_register_hcd - Register Host controller to OTG core
+ * @hcd: Host controller device
+ * @irqnum: interrupt number
+ * @irqflags: interrupt flags
+ * @ops: HCD ops to add/remove 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.
+ *
+ * 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 *otgd;
+ struct device *hcd_dev = hcd->self.controller;
+ struct device *otg_dev = usb_otg_get_device(hcd_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);
+ otgd = usb_otg_get_data(otg_dev);
+ mutex_unlock(&otg_list_mutex);
+ if (!otgd) {
+ dev_dbg(hcd_dev,
+ "otg: controller not yet registered. waiting..\n");
+ /*
+ * otg controller might register later. Put the hcd in
+ * wait list and call us back when ready
+ */
+ if (usb_otg_hcd_wait_add(otg_dev, hcd, irqnum, irqflags, ops)) {
+ dev_dbg(hcd_dev, "otg: failed to add to wait list\n");
+ return -EINVAL;
+ }
+
+ return 0;
+ }
+
+ /* HCD will be started by OTG fsm when needed */
+ mutex_lock(&otgd->fsm.lock);
+ if (otgd->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 == otgd->primary_hcd.hcd) {
+ if (otgd->shared_hcd.hcd) {
+ dev_err(otg_dev, "otg: shared host already registered\n");
+ goto err;
+ }
+
+ otgd->shared_hcd.hcd = hcd;
+ otgd->shared_hcd.irqnum = irqnum;
+ otgd->shared_hcd.irqflags = irqflags;
+ otgd->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;
+ }
+
+ otgd->primary_hcd.hcd = hcd;
+ otgd->primary_hcd.irqnum = irqnum;
+ otgd->primary_hcd.irqflags = irqflags;
+ otgd->primary_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 (otgd->shared_hcd.hcd || !otgd->primary_hcd.hcd->shared_hcd) {
+ otgd->fsm.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(&otgd->fsm);
+ } else {
+ dev_dbg(otg_dev, "otg: can't start till shared host registers\n");
+ }
+
+ mutex_unlock(&otgd->fsm.lock);
+
+ return 0;
+
+err:
+ mutex_unlock(&otgd->fsm.lock);
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(usb_otg_register_hcd);
+
+/**
+ * usb_otg_unregister_hcd - Unregister 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 *otgd;
+ struct device *hcd_dev = hcd_to_bus(hcd)->controller;
+ struct device *otg_dev = usb_otg_get_device(hcd_dev);
+
+ if (!otg_dev)
+ return -EINVAL; /* we're definitely not OTG */
+
+ mutex_lock(&otg_list_mutex);
+ otgd = usb_otg_get_data(otg_dev);
+ mutex_unlock(&otg_list_mutex);
+ if (!otgd) {
+ /* are we in wait list? */
+ if (!usb_otg_hcd_wait_remove(hcd))
+ return 0;
+
+ dev_dbg(hcd_dev, "otg: host wasn't registered with otg\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&otgd->fsm.lock);
+ if (hcd == otgd->primary_hcd.hcd) {
+ otgd->primary_hcd.hcd = NULL;
+ dev_info(otg_dev, "otg: primary host %s unregistered\n",
+ dev_name(hcd_dev));
+ } else if (hcd == otgd->shared_hcd.hcd) {
+ otgd->shared_hcd.hcd = NULL;
+ dev_info(otg_dev, "otg: shared host %s unregistered\n",
+ dev_name(hcd_dev));
+ } else {
+ dev_err(otg_dev, "otg: host %s wasn't registered with otg\n",
+ dev_name(hcd_dev));
+ mutex_unlock(&otgd->fsm.lock);
+ return -EINVAL;
+ }
+
+ /* stop FSM & Host */
+ usb_otg_stop_fsm(&otgd->fsm);
+ otgd->fsm.otg->host = NULL;
+
+ mutex_unlock(&otgd->fsm.lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usb_otg_unregister_hcd);
+
+/**
+ * usb_otg_register_gadget - Register 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 *otgd;
+ struct device *gadget_dev = &gadget->dev;
+ struct device *otg_dev = usb_otg_get_device(gadget_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);
+ otgd = usb_otg_get_data(otg_dev);
+ mutex_unlock(&otg_list_mutex);
+ if (!otgd) {
+ dev_dbg(gadget_dev,
+ "otg: controller not yet registered. waiting..\n");
+ /*
+ * otg controller might register later. Put the gadget in
+ * wait list and call us back when ready
+ */
+ if (usb_otg_gadget_wait_add(otg_dev, gadget, ops)) {
+ dev_dbg(gadget_dev, "otg: failed to add to wait list\n");
+ return -EINVAL;
+ }
+
+ return 0;
+ }
+
+ mutex_lock(&otgd->fsm.lock);
+ if (otgd->fsm.otg->gadget) {
+ dev_err(otg_dev, "otg: gadget already registered with otg\n");
+ mutex_unlock(&otgd->fsm.lock);
+ return -EINVAL;
+ }
+
+ otgd->fsm.otg->gadget = gadget;
+ otgd->gadget_ops = ops;
+ dev_info(otg_dev, "otg: gadget %s registered\n",
+ dev_name(&gadget->dev));
+
+ /* start FSM */
+ usb_otg_start_fsm(&otgd->fsm);
+ mutex_unlock(&otgd->fsm.lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usb_otg_register_gadget);
+
+/**
+ * usb_otg_unregister_gadget - Unregister 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 not running
+ * on successful return.
+ *
+ * Returns: 0 on success, error value otherwise.
+ */
+int usb_otg_unregister_gadget(struct usb_gadget *gadget)
+{
+ struct usb_otg *otgd;
+ struct device *gadget_dev = &gadget->dev;
+ struct device *otg_dev = usb_otg_get_device(gadget_dev);
+
+ if (!otg_dev)
+ return -EINVAL;
+
+ mutex_lock(&otg_list_mutex);
+ otgd = usb_otg_get_data(otg_dev);
+ mutex_unlock(&otg_list_mutex);
+ if (!otgd) {
+ /* are we in wait list? */
+ if (!usb_otg_gadget_wait_remove(gadget))
+ return 0;
+
+ dev_dbg(gadget_dev, "otg: gadget wasn't registered with otg\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&otgd->fsm.lock);
+ if (otgd->fsm.otg->gadget != gadget) {
+ dev_err(otg_dev, "otg: gadget %s wasn't registered with otg\n",
+ dev_name(&gadget->dev));
+ mutex_unlock(&otgd->fsm.lock);
+ return -EINVAL;
+ }
+
+ /* Stop FSM & gadget */
+ usb_otg_stop_fsm(&otgd->fsm);
+ otgd->fsm.otg->gadget = NULL;
+ mutex_unlock(&otgd->fsm.lock);
+
+ dev_info(otg_dev, "otg: gadget %s unregistered\n",
+ dev_name(&gadget->dev));
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usb_otg_unregister_gadget);
+
+/**
+ * usb_otg_fsm_to_dev - Get OTG controller device from struct otg_fsm
+ * @fsm: otg_fsm data structure
+ *
+ * This is used by the OTG controller driver to get it's device node
+ * from any of the otg_fsm->ops.
+ */
+struct device *usb_otg_fsm_to_dev(struct otg_fsm *fsm)
+{
+ struct usb_otg *otgd = container_of(fsm, struct usb_otg, fsm);
+
+ return otgd->dev;
+}
+EXPORT_SYMBOL_GPL(usb_otg_fsm_to_dev);
diff --git a/drivers/usb/common/usb-otg.h b/drivers/usb/common/usb-otg.h
new file mode 100644
index 0000000..05331f0
--- /dev/null
+++ b/drivers/usb/common/usb-otg.h
@@ -0,0 +1,71 @@
+/**
+ * drivers/usb/common/usb-otg.h - USB OTG core local header
+ *
+ * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com
+ * Author: Roger Quadros <[email protected]>
+ *
+ * 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.
+ */
+
+#ifndef __DRIVERS_USB_COMMON_USB_OTG_H
+#define __DRIVERS_USB_COMMON_USB_OTG_H
+
+/*
+ * A-DEVICE timing constants
+ */
+
+/* Wait for VBUS Rise */
+#define TA_WAIT_VRISE (100) /* a_wait_vrise: section 7.1.2
+ * a_wait_vrise_tmr: section 7.4.5.1
+ * TA_VBUS_RISE <= 100ms, section 4.4
+ * Table 4-1: Electrical Characteristics
+ * ->DC Electrical Timing
+ */
+/* Wait for VBUS Fall */
+#define TA_WAIT_VFALL (1000) /* a_wait_vfall: section 7.1.7
+ * a_wait_vfall_tmr: section: 7.4.5.2
+ */
+/* Wait for B-Connect */
+#define TA_WAIT_BCON (10000) /* a_wait_bcon: section 7.1.3
+ * TA_WAIT_BCON: should be between 1100
+ * and 30000 ms, section 5.5, Table 5-1
+ */
+/* A-Idle to B-Disconnect */
+#define TA_AIDL_BDIS (5000) /* a_suspend min 200 ms, section 5.2.1
+ * TA_AIDL_BDIS: section 5.5, Table 5-1
+ */
+/* B-Idle to A-Disconnect */
+#define TA_BIDL_ADIS (500) /* TA_BIDL_ADIS: section 5.2.1
+ * 500ms is used for B switch to host
+ * for safe
+ */
+
+/*
+ * B-device timing constants
+ */
+
+/* Data-Line Pulse Time*/
+#define TB_DATA_PLS (10) /* b_srp_init,continue 5~10ms
+ * section:5.1.3
+ */
+/* SRP Fail Time */
+#define TB_SRP_FAIL (6000) /* b_srp_init,fail time 5~6s
+ * section:5.1.6
+ */
+/* A-SE0 to B-Reset */
+#define TB_ASE0_BRST (155) /* minimum 155 ms, section:5.3.1 */
+/* SE0 Time Before SRP */
+#define TB_SE0_SRP (1000) /* b_idle,minimum 1s, section:5.1.2 */
+/* SSEND time before SRP */
+#define TB_SSEND_SRP (1500) /* minimum 1.5 sec, section:5.1.2 */
+
+#define TB_SESS_VLD (1000)
+
+#endif /* __DRIVERS_USB_COMMON_USB_OTG_H */
diff --git a/drivers/usb/core/Kconfig b/drivers/usb/core/Kconfig
index a99c89e..b468a9f 100644
--- a/drivers/usb/core/Kconfig
+++ b/drivers/usb/core/Kconfig
@@ -42,7 +42,7 @@ config USB_DYNAMIC_MINORS
If you are unsure about this, say N here.

config USB_OTG
- bool "OTG support"
+ bool "OTG/Dual-role support"
depends on PM
default n
help
@@ -75,15 +75,6 @@ config USB_OTG_BLACKLIST_HUB
and software costs by not supporting external hubs. So
are "Embedded Hosts" that don't offer OTG support.

-config USB_OTG_FSM
- tristate "USB 2.0 OTG FSM implementation"
- depends on USB
- select USB_OTG
- select USB_PHY
- help
- Implements OTG Finite State Machine as specified in On-The-Go
- and Embedded Host Supplement to the USB Revision 2.0 Specification.
-
config USB_ULPI_BUS
tristate "USB ULPI PHY interface support"
depends on USB_SUPPORT
diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h
index bd1dcf8..38cabe0 100644
--- a/include/linux/usb/otg.h
+++ b/include/linux/usb/otg.h
@@ -10,19 +10,100 @@
#define __LINUX_USB_OTG_H

#include <linux/phy/phy.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
+ */
+struct otg_hcd {
+ struct usb_hcd *hcd;
+ unsigned int irqnum;
+ unsigned long irqflags;
+ struct otg_hcd_ops *ops;
+};
+
+struct usb_otg;
+
+/**
+ * struct otg_timer - otg timer data
+ *
+ * @timer: high resolution timer
+ * @timeout: timeout value
+ * @timetout_bit: pointer to variable that is set on timeout
+ * @otgd: usb otg data
+ */
+struct otg_timer {
+ struct hrtimer timer;
+ ktime_t timeout;
+ /* callback data */
+ int *timeout_bit;
+ struct usb_otg *otgd;
+};
+
+/**
+ * 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
+ * @fsm_ops: controller hooks for the state machine
+ * ------- internal use only -------
+ * @primary_hcd: primary host state and interface
+ * @shared_hcd: shared host state and interface
+ * @gadget_ops: gadget interface
+ * @timers: otg timers for state machine
+ * @list: list of otg controllers
+ * @work: otg state machine work
+ * @wq: otg state machine work queue
+ * @fsm_running: state machine running/stopped indicator
+ */
struct usb_otg {
u8 default_a;

struct phy *phy;
/* old usb_phy interface */
struct usb_phy *usb_phy;
+
struct usb_bus *host;
struct usb_gadget *gadget;

enum usb_otg_state state;

+ struct device *dev;
+ struct usb_otg_caps *caps;
+ struct otg_fsm fsm;
+ struct otg_fsm_ops fsm_ops;
+
+ /* internal use only */
+ struct otg_hcd primary_hcd;
+ struct otg_hcd shared_hcd;
+ struct otg_gadget_ops *gadget_ops;
+ struct otg_timer timers[NUM_OTG_FSM_TIMERS];
+ struct list_head list;
+ struct work_struct work;
+ struct workqueue_struct *wq;
+ bool fsm_running;
+ /* 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);

@@ -38,7 +119,7 @@ struct usb_otg {

/* start or continue HNP role switch */
int (*start_hnp)(struct usb_otg *otg);
-
+/*---------------------------------------------------------------*/
};

/**
@@ -56,8 +137,105 @@ struct usb_otg_caps {
bool adp_support;
};

+/**
+ * struct usb_otg_config - otg controller configuration
+ * @caps: otg capabilities of the controller
+ * @ops: otg fsm operations
+ * @otg_timeouts: override default otg fsm timeouts
+ */
+struct usb_otg_config {
+ struct usb_otg_caps otg_caps;
+ struct otg_fsm_ops *fsm_ops;
+ unsigned otg_timeouts[NUM_OTG_FSM_TIMERS];
+};
+
extern const char *usb_otg_state_string(enum usb_otg_state state);

+enum usb_dr_mode {
+ USB_DR_MODE_UNKNOWN,
+ USB_DR_MODE_HOST,
+ USB_DR_MODE_PERIPHERAL,
+ USB_DR_MODE_OTG,
+};
+
+#if IS_ENABLED(CONFIG_USB_OTG)
+struct otg_fsm *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 otg_fsm *fsm);
+int usb_otg_kick_fsm(struct device *hcd_gcd_device);
+struct device *usb_otg_fsm_to_dev(struct otg_fsm *fsm);
+int usb_otg_start_host(struct otg_fsm *fsm, int on);
+int usb_otg_start_gadget(struct otg_fsm *fsm, int on);
+
+#else /* CONFIG_USB_OTG */
+
+static inline struct otg_fsm *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 otg_fsm *fsm)
+{
+}
+
+static inline int usb_otg_kick_fsm(struct device *hcd_gcd_device)
+{
+ return -ENOTSUPP;
+}
+
+static inline struct device *usb_otg_fsm_to_dev(struct otg_fsm *fsm)
+{
+ return NULL;
+}
+
+static inline int usb_otg_start_host(struct otg_fsm *fsm, int on)
+{
+ return -ENOTSUPP;
+}
+
+static inline int usb_otg_start_gadget(struct otg_fsm *fsm, int on)
+{
+ return -ENOTSUPP;
+}
+#endif /* CONFIG_USB_OTG */
+
+/*------------- deprecated interface -----------------------------*/
/* Context: can sleep */
static inline int
otg_start_hnp(struct usb_otg *otg)
@@ -109,14 +287,9 @@ 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);

-enum usb_dr_mode {
- USB_DR_MODE_UNKNOWN,
- USB_DR_MODE_HOST,
- USB_DR_MODE_PERIPHERAL,
- USB_DR_MODE_OTG,
-};
-
#endif /* __LINUX_USB_OTG_H */
--
2.1.4

2015-08-24 13:22:21

by Roger Quadros

[permalink] [raw]
Subject: [PATCH v4 08/13] usb: doc: dt-binding: Add otg-controller property

The otg-controller property is used to link the host/gadget
controllers to the otg controller. otg-controller property must
contain the phandle of the otg controller.

The otg core uses this property to tie the host/gadget
controllres to the correct otg controller.

Signed-off-by: Roger Quadros <[email protected]>
---
Documentation/devicetree/bindings/usb/generic.txt | 5 +++++
1 file changed, 5 insertions(+)

diff --git a/Documentation/devicetree/bindings/usb/generic.txt b/Documentation/devicetree/bindings/usb/generic.txt
index bba8257..5a457bc 100644
--- a/Documentation/devicetree/bindings/usb/generic.txt
+++ b/Documentation/devicetree/bindings/usb/generic.txt
@@ -24,6 +24,11 @@ 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. If this property is not present then otg
+ core assumes that host and gadget controllers are
+ children of the otg controller.

This is an attribute to a USB controller such as:

--
2.1.4

2015-08-24 13:24:32

by Roger Quadros

[permalink] [raw]
Subject: [PATCH v4 09/13] usb: chipidea: move from CONFIG_USB_OTG_FSM to CONFIG_USB_OTG

CONFIG_USB_OTG_FSM no longer exists and the OTG FSM
is available if CONFIG_USB_OTG is defined so move
to using CONFIG_USB_OTG.

Signed-off-by: Roger Quadros <[email protected]>
---
Documentation/usb/chipidea.txt | 2 +-
drivers/usb/chipidea/Makefile | 2 +-
drivers/usb/chipidea/ci.h | 2 +-
drivers/usb/chipidea/otg_fsm.h | 2 +-
drivers/usb/phy/Kconfig | 2 +-
5 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/Documentation/usb/chipidea.txt b/Documentation/usb/chipidea.txt
index 3f848c1..e7f97da 100644
--- a/Documentation/usb/chipidea.txt
+++ b/Documentation/usb/chipidea.txt
@@ -5,7 +5,7 @@ with 2 Freescale i.MX6Q sabre SD boards.

1.1 How to enable OTG FSM in menuconfig
---------------------------------------
-Select CONFIG_USB_OTG_FSM, rebuild kernel Image and modules.
+Select CONFIG_USB_OTG, rebuild kernel Image and modules.
If you want to check some internal variables for otg fsm,
select CONFIG_USB_CHIPIDEA_DEBUG, there are 2 files which
can show otg fsm variables and some controller registers value:
diff --git a/drivers/usb/chipidea/Makefile b/drivers/usb/chipidea/Makefile
index 4decb12..63f2394 100644
--- a/drivers/usb/chipidea/Makefile
+++ b/drivers/usb/chipidea/Makefile
@@ -6,7 +6,7 @@ ci_hdrc-y := core.o otg.o
ci_hdrc-$(CONFIG_USB_CHIPIDEA_UDC) += udc.o
ci_hdrc-$(CONFIG_USB_CHIPIDEA_HOST) += host.o
ci_hdrc-$(CONFIG_USB_CHIPIDEA_DEBUG) += debug.o
-ci_hdrc-$(CONFIG_USB_OTG_FSM) += otg_fsm.o
+ci_hdrc-$(CONFIG_USB_OTG) += otg_fsm.o

# Glue/Bridge layers go here

diff --git a/drivers/usb/chipidea/ci.h b/drivers/usb/chipidea/ci.h
index 41d7cf6..438818c 100644
--- a/drivers/usb/chipidea/ci.h
+++ b/drivers/usb/chipidea/ci.h
@@ -407,7 +407,7 @@ static inline u32 hw_test_and_write(struct ci_hdrc *ci, enum ci_hw_regs reg,
*/
static inline bool ci_otg_is_fsm_mode(struct ci_hdrc *ci)
{
-#ifdef CONFIG_USB_OTG_FSM
+#ifdef CONFIG_USB_OTG
struct usb_otg_caps *otg_caps = &ci->platdata->ci_otg_caps;

return ci->is_otg && ci->roles[CI_ROLE_HOST] &&
diff --git a/drivers/usb/chipidea/otg_fsm.h b/drivers/usb/chipidea/otg_fsm.h
index 2689375..fb66695 100644
--- a/drivers/usb/chipidea/otg_fsm.h
+++ b/drivers/usb/chipidea/otg_fsm.h
@@ -62,7 +62,7 @@
/* SSEND time before SRP */
#define TB_SSEND_SRP (1500) /* minimum 1.5 sec, section:5.1.2 */

-#ifdef CONFIG_USB_OTG_FSM
+#ifdef CONFIG_USB_OTG

int ci_hdrc_otg_fsm_init(struct ci_hdrc *ci);
int ci_otg_fsm_work(struct ci_hdrc *ci);
diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig
index 7d3beee..664cacb 100644
--- a/drivers/usb/phy/Kconfig
+++ b/drivers/usb/phy/Kconfig
@@ -20,7 +20,7 @@ config AB8500_USB

config FSL_USB2_OTG
bool "Freescale USB OTG Transceiver Driver"
- depends on USB_EHCI_FSL && USB_FSL_USB2 && USB_OTG_FSM && PM
+ depends on USB_EHCI_FSL && USB_FSL_USB2 && PM
select USB_OTG
select USB_PHY
help
--
2.1.4

2015-08-24 13:22:23

by Roger Quadros

[permalink] [raw]
Subject: [PATCH v4 10/13] usb: hcd: Adapt to OTG core

The existing usb_add/remove_hcd() functionality
remains unchanged for non-OTG devices. For OTG
devices they only register the HCD with the OTG core.

Introduce usb_otg_add/remove_hcd() for use by OTG core.
These functions actually add/remove the HCD.

Signed-off-by: Roger Quadros <[email protected]>
---
drivers/usb/core/hcd.c | 55 +++++++++++++++++++++++++++++++++++++++++++++-----
1 file changed, 50 insertions(+), 5 deletions(-)

diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index c0fd1f6..4851682 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -46,6 +46,7 @@
#include <linux/usb.h>
#include <linux/usb/hcd.h>
#include <linux/usb/phy.h>
+#include <linux/usb/otg.h>

#include "usb.h"

@@ -2625,8 +2626,8 @@ static void usb_put_invalidate_rhdev(struct usb_hcd *hcd)
* buffers of consistent memory, register the bus, request the IRQ line,
* and call the driver's reset() and start() routines.
*/
-int usb_add_hcd(struct usb_hcd *hcd,
- unsigned int irqnum, unsigned long irqflags)
+static int usb_otg_add_hcd(struct usb_hcd *hcd,
+ unsigned int irqnum, unsigned long irqflags)
{
int retval;
struct usb_device *rhdev;
@@ -2839,17 +2840,16 @@ err_phy:
}
return retval;
}
-EXPORT_SYMBOL_GPL(usb_add_hcd);

/**
- * usb_remove_hcd - shutdown processing for generic HCDs
+ * usb_otg_remove_hcd - shutdown processing for generic HCDs
* @hcd: the usb_hcd structure to remove
* Context: !in_interrupt()
*
* Disconnects the root hub, then reverses the effects of usb_add_hcd(),
* invoking the HCD's stop() method.
*/
-void usb_remove_hcd(struct usb_hcd *hcd)
+static void usb_otg_remove_hcd(struct usb_hcd *hcd)
{
struct usb_device *rhdev = hcd->self.root_hub;

@@ -2923,6 +2923,51 @@ void usb_remove_hcd(struct usb_hcd *hcd)

usb_put_invalidate_rhdev(hcd);
}
+
+static struct otg_hcd_ops otg_hcd_intf = {
+ .add = usb_otg_add_hcd,
+ .remove = usb_otg_remove_hcd,
+};
+
+/**
+ * usb_add_hcd - finish generic HCD structure initialization and register
+ * @hcd: the usb_hcd structure to initialize
+ * @irqnum: Interrupt line to allocate
+ * @irqflags: Interrupt type flags
+ *
+ * Finish the remaining parts of generic HCD initialization: allocate the
+ * buffers of consistent memory, register the bus, request the IRQ line,
+ * and call the driver's reset() and start() routines.
+ * If it is an OTG device then it only registers the HCD with OTG core.
+ *
+ */
+int usb_add_hcd(struct usb_hcd *hcd,
+ unsigned int irqnum, unsigned long irqflags)
+{
+ /* If OTG device, OTG core takes care of adding HCD */
+ if (usb_otg_register_hcd(hcd, irqnum, irqflags, &otg_hcd_intf))
+ return usb_otg_add_hcd(hcd, irqnum, irqflags);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usb_add_hcd);
+
+/**
+ * usb_remove_hcd - shutdown processing for generic HCDs
+ * @hcd: the usb_hcd structure to remove
+ * Context: !in_interrupt()
+ *
+ * Disconnects the root hub, then reverses the effects of usb_add_hcd(),
+ * invoking the HCD's stop() method.
+ * If it is an OTG device then it unregisters the HCD from OTG core
+ * as well.
+ */
+void usb_remove_hcd(struct usb_hcd *hcd)
+{
+ /* If OTG device, OTG core takes care of stopping HCD */
+ if (usb_otg_unregister_hcd(hcd))
+ usb_otg_remove_hcd(hcd);
+}
EXPORT_SYMBOL_GPL(usb_remove_hcd);

void
--
2.1.4

2015-08-24 13:22:26

by Roger Quadros

[permalink] [raw]
Subject: [PATCH v4 11/13] usb: core: hub: Notify OTG fsm when A device sets b_hnp_enable

This is the a_set_b_hnp_enable flag in the OTG state machine
diagram and must be set when the A-Host has successfully set
the b_hnp_enable feature of the OTG-B-Peripheral attached to it.

When this bit changes we kick our OTG FSM to make note of the
change and act accordingly.

Signed-off-by: Roger Quadros <[email protected]>
---
drivers/usb/core/hub.c | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 431839b..3993168 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -2271,6 +2271,9 @@ static int usb_enumerate_device_otg(struct usb_device *udev)
"can't set HNP mode: %d\n",
err);
bus->b_hnp_enable = 0;
+ } else {
+ /* notify OTG fsm about a_set_b_hnp_enable */
+ usb_otg_kick_fsm(udev->bus->controller);
}
}
}
@@ -4238,8 +4241,13 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
*/
if (!hdev->parent) {
delay = HUB_ROOT_RESET_TIME;
- if (port1 == hdev->bus->otg_port)
+ if (port1 == hdev->bus->otg_port) {
hdev->bus->b_hnp_enable = 0;
+#ifdef CONFIG_USB_OTG
+ /* notify OTG fsm about a_set_b_hnp_enable change */
+ usb_otg_kick_fsm(hdev->bus->controller);
+#endif
+ }
}

/* Some low speed devices have problems with the quick delay, so */
--
2.1.4

2015-08-24 13:23:48

by Roger Quadros

[permalink] [raw]
Subject: [PATCH v4 12/13] usb: gadget: udc: adapt to OTG core

The OTG state machine needs a mechanism to start and
stop the gadget controller. Add usb_gadget_start()
and usb_gadget_stop().

Register with OTG core when gadget function driver
is available and unregister when function driver is unbound.

We need to unlock the usb_lock mutexbefore calling
usb_otg_register_gadget() in udc_bind_to_driver() and
usb_gadget_remove_driver() else it will cause a circular
locking dependency.

Ignore softconnect sysfs control when we're in OTG
mode as OTG FSM takes care of gadget softconnect using
the b_bus_req mechanism.

Signed-off-by: Roger Quadros <[email protected]>
---
drivers/usb/gadget/udc/udc-core.c | 124 +++++++++++++++++++++++++++++++++++---
1 file changed, 114 insertions(+), 10 deletions(-)

diff --git a/drivers/usb/gadget/udc/udc-core.c b/drivers/usb/gadget/udc/udc-core.c
index f660afb..e961668 100644
--- a/drivers/usb/gadget/udc/udc-core.c
+++ b/drivers/usb/gadget/udc/udc-core.c
@@ -28,6 +28,7 @@
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
#include <linux/usb.h>
+#include <linux/usb/otg.h>

/**
* struct usb_udc - describes one usb device controller
@@ -37,6 +38,7 @@
* @list - for use by the udc class driver
* @vbus - for udcs who care about vbus status, this value is real vbus status;
* for udcs who do not care about vbus status, this value is always true
+ * @is_otg - we're registered with OTG core and it takes care of UDC start/stop
*
* This represents the internal data structure which is used by the UDC-class
* to hold information about udc driver and gadget together.
@@ -47,6 +49,7 @@ struct usb_udc {
struct device dev;
struct list_head list;
bool vbus;
+ bool is_otg;
};

static struct class *udc_class;
@@ -300,6 +303,7 @@ EXPORT_SYMBOL_GPL(usb_gadget_udc_reset);
*/
static inline int usb_gadget_udc_start(struct usb_udc *udc)
{
+ dev_dbg(&udc->dev, "%s\n", __func__);
return udc->gadget->ops->udc_start(udc->gadget, udc->driver);
}

@@ -317,10 +321,81 @@ static inline int usb_gadget_udc_start(struct usb_udc *udc)
*/
static inline void usb_gadget_udc_stop(struct usb_udc *udc)
{
+ dev_dbg(&udc->dev, "%s\n", __func__);
udc->gadget->ops->udc_stop(udc->gadget);
}

/**
+ * usb_gadget_start - start the usb gadget controller and connect to bus
+ * @gadget: the gadget device to start
+ *
+ * This is external API for use by OTG core.
+ *
+ * Start the usb device controller and connect to bus (enable pull).
+ */
+static int usb_gadget_start(struct usb_gadget *gadget)
+{
+ int ret;
+ struct usb_udc *udc = NULL;
+
+ dev_dbg(&gadget->dev, "%s\n", __func__);
+ mutex_lock(&udc_lock);
+ list_for_each_entry(udc, &udc_list, list)
+ if (udc->gadget == gadget)
+ goto found;
+
+ dev_err(gadget->dev.parent, "%s: gadget not registered.\n",
+ __func__);
+ mutex_unlock(&udc_lock);
+ return -EINVAL;
+
+found:
+ ret = usb_gadget_udc_start(udc);
+ if (ret)
+ dev_err(&udc->dev, "USB Device Controller didn't start: %d\n",
+ ret);
+ else
+ usb_udc_connect_control(udc);
+
+ mutex_unlock(&udc_lock);
+
+ return ret;
+}
+
+/**
+ * usb_gadget_stop - disconnect from bus and stop the usb gadget
+ * @gadget: The gadget device we want to stop
+ *
+ * This is external API for use by OTG core.
+ *
+ * Disconnect from the bus (disable pull) and stop the
+ * gadget controller.
+ */
+static int usb_gadget_stop(struct usb_gadget *gadget)
+{
+ struct usb_udc *udc = NULL;
+
+ dev_dbg(&gadget->dev, "%s\n", __func__);
+ mutex_lock(&udc_lock);
+ list_for_each_entry(udc, &udc_list, list)
+ if (udc->gadget == gadget)
+ goto found;
+
+ dev_err(gadget->dev.parent, "%s: gadget not registered.\n",
+ __func__);
+ mutex_unlock(&udc_lock);
+ return -EINVAL;
+
+found:
+ usb_gadget_disconnect(udc->gadget);
+ udc->driver->disconnect(udc->gadget);
+ usb_gadget_udc_stop(udc);
+ mutex_unlock(&udc_lock);
+
+ return 0;
+}
+
+/**
* usb_udc_release - release the usb_udc struct
* @dev: the dev member within usb_udc
*
@@ -438,6 +513,7 @@ int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget)
}
EXPORT_SYMBOL_GPL(usb_add_gadget_udc);

+/* udc_lock must be held */
static void usb_gadget_remove_driver(struct usb_udc *udc)
{
dev_dbg(&udc->dev, "unregistering UDC driver [%s]\n",
@@ -445,10 +521,18 @@ static void usb_gadget_remove_driver(struct usb_udc *udc)

kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE);

- usb_gadget_disconnect(udc->gadget);
- udc->driver->disconnect(udc->gadget);
+ /* If OTG, the otg core ensures UDC is stopped on unregister */
+ if (udc->is_otg) {
+ mutex_unlock(&udc_lock);
+ usb_otg_unregister_gadget(udc->gadget);
+ mutex_lock(&udc_lock);
+ } else {
+ usb_gadget_disconnect(udc->gadget);
+ udc->driver->disconnect(udc->gadget);
+ usb_gadget_udc_stop(udc);
+ }
+
udc->driver->unbind(udc->gadget);
- usb_gadget_udc_stop(udc);

udc->driver = NULL;
udc->dev.driver = NULL;
@@ -473,11 +557,12 @@ void usb_del_gadget_udc(struct usb_gadget *gadget)

mutex_lock(&udc_lock);
list_del(&udc->list);
- mutex_unlock(&udc_lock);

if (udc->driver)
usb_gadget_remove_driver(udc);

+ mutex_unlock(&udc_lock);
+
kobject_uevent(&udc->dev.kobj, KOBJ_REMOVE);
flush_work(&gadget->work);
device_unregister(&udc->dev);
@@ -487,6 +572,12 @@ EXPORT_SYMBOL_GPL(usb_del_gadget_udc);

/* ------------------------------------------------------------------------- */

+struct otg_gadget_ops otg_gadget_intf = {
+ .start = usb_gadget_start,
+ .stop = usb_gadget_stop,
+};
+
+/* udc_lock must be held */
static int udc_bind_to_driver(struct usb_udc *udc, struct usb_gadget_driver *driver)
{
int ret;
@@ -501,12 +592,19 @@ static int udc_bind_to_driver(struct usb_udc *udc, struct usb_gadget_driver *dri
ret = driver->bind(udc->gadget, driver);
if (ret)
goto err1;
- ret = usb_gadget_udc_start(udc);
- if (ret) {
- driver->unbind(udc->gadget);
- goto err1;
+
+ /* If OTG, the otg core starts the UDC when needed */
+ mutex_unlock(&udc_lock);
+ udc->is_otg = !usb_otg_register_gadget(udc->gadget, &otg_gadget_intf);
+ mutex_lock(&udc_lock);
+ if (!udc->is_otg) {
+ ret = usb_gadget_udc_start(udc);
+ if (ret) {
+ driver->unbind(udc->gadget);
+ goto err1;
+ }
+ usb_udc_connect_control(udc);
}
- usb_udc_connect_control(udc);

kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE);
return 0;
@@ -618,9 +716,15 @@ static ssize_t usb_udc_softconn_store(struct device *dev,
return -EOPNOTSUPP;
}

+ /* In OTG mode we don't support softconnect, but b_bus_req */
+ if (udc->is_otg) {
+ dev_err(dev, "soft-connect not supported in OTG mode\n");
+ return -EOPNOTSUPP;
+ }
+
if (sysfs_streq(buf, "connect")) {
usb_gadget_udc_start(udc);
- usb_gadget_connect(udc->gadget);
+ usb_udc_connect_control(udc);
} else if (sysfs_streq(buf, "disconnect")) {
usb_gadget_disconnect(udc->gadget);
udc->driver->disconnect(udc->gadget);
--
2.1.4

2015-08-24 13:22:28

by Roger Quadros

[permalink] [raw]
Subject: [PATCH v4 13/13] usb: otg: Add dual-role device (DRD) support

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 <[email protected]>
---
drivers/usb/common/usb-otg.c | 178 +++++++++++++++++++++++++++++++++++++++++--
include/linux/usb/otg-fsm.h | 5 ++
include/linux/usb/otg.h | 2 +
3 files changed, 177 insertions(+), 8 deletions(-)

diff --git a/drivers/usb/common/usb-otg.c b/drivers/usb/common/usb-otg.c
index 930c2fe..44b5577 100644
--- a/drivers/usb/common/usb-otg.c
+++ b/drivers/usb/common/usb-otg.c
@@ -519,14 +519,165 @@ int usb_otg_start_gadget(struct otg_fsm *fsm, int on)
}
EXPORT_SYMBOL_GPL(usb_otg_start_gadget);

+/* Change USB protocol when there is a protocol change */
+static int drd_set_protocol(struct otg_fsm *fsm, int protocol)
+{
+ struct usb_otg *otgd = container_of(fsm, struct usb_otg, fsm);
+ int ret = 0;
+
+ if (fsm->protocol != protocol) {
+ dev_dbg(otgd->dev, "otg: changing role fsm->protocol= %d; new protocol= %d\n",
+ fsm->protocol, protocol);
+ /* stop old protocol */
+ if (fsm->protocol == PROTO_HOST)
+ ret = otg_start_host(fsm, 0);
+ else if (fsm->protocol == PROTO_GADGET)
+ ret = otg_start_gadget(fsm, 0);
+ if (ret)
+ return ret;
+
+ /* start new protocol */
+ if (protocol == PROTO_HOST)
+ ret = otg_start_host(fsm, 1);
+ else if (protocol == PROTO_GADGET)
+ ret = otg_start_gadget(fsm, 1);
+ if (ret)
+ return ret;
+
+ fsm->protocol = protocol;
+ return 0;
+ }
+
+ return 0;
+}
+
+/* Called when entering a DRD state */
+static void drd_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state)
+{
+ struct usb_otg *otgd = container_of(fsm, struct usb_otg, fsm);
+
+ if (fsm->otg->state == new_state)
+ return;
+
+ fsm->state_changed = 1;
+ dev_dbg(otgd->dev, "otg: set state: %s\n",
+ usb_otg_state_string(new_state));
+ switch (new_state) {
+ case OTG_STATE_B_IDLE:
+ drd_set_protocol(fsm, PROTO_UNDEF);
+ break;
+ case OTG_STATE_B_PERIPHERAL:
+ drd_set_protocol(fsm, PROTO_GADGET);
+ break;
+ case OTG_STATE_A_HOST:
+ drd_set_protocol(fsm, PROTO_HOST);
+ break;
+ case OTG_STATE_UNDEFINED:
+ case OTG_STATE_B_SRP_INIT:
+ case OTG_STATE_B_WAIT_ACON:
+ case OTG_STATE_B_HOST:
+ case OTG_STATE_A_IDLE:
+ case OTG_STATE_A_WAIT_VRISE:
+ case OTG_STATE_A_WAIT_BCON:
+ case OTG_STATE_A_SUSPEND:
+ case OTG_STATE_A_PERIPHERAL:
+ case OTG_STATE_A_WAIT_VFALL:
+ case OTG_STATE_A_VBUS_ERR:
+ default:
+ dev_warn(otgd->dev, "%s: otg: invalid state: %s\n",
+ __func__, usb_otg_state_string(new_state));
+ break;
+ }
+
+ fsm->otg->state = new_state;
+}
+
/**
- * OTG FSM work function
+ * DRD state change judgement
+ *
+ * For DRD we're only interested in some of the OTG states
+ * i.e. OTG_STATE_B_IDLE: both peripheral and host are stopped
+ * OTG_STATE_B_PERIPHERAL: peripheral active
+ * OTG_STATE_A_HOST: host active
+ * we're only interested in the following inputs
+ * fsm->id, fsm->b_sess_vld
+ */
+static int drd_statemachine(struct otg_fsm *fsm)
+{
+ struct usb_otg *otgd = container_of(fsm, struct usb_otg, fsm);
+ enum usb_otg_state state;
+
+ mutex_lock(&fsm->lock);
+
+ state = fsm->otg->state;
+
+ switch (state) {
+ case OTG_STATE_UNDEFINED:
+ if (!fsm->id)
+ drd_set_state(fsm, OTG_STATE_A_HOST);
+ else if (fsm->id && fsm->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;
+
+ /* invalid states for DRD */
+ case OTG_STATE_B_SRP_INIT:
+ case OTG_STATE_B_WAIT_ACON:
+ case OTG_STATE_B_HOST:
+ case OTG_STATE_A_IDLE:
+ case OTG_STATE_A_WAIT_VRISE:
+ case OTG_STATE_A_WAIT_BCON:
+ case OTG_STATE_A_SUSPEND:
+ case OTG_STATE_A_PERIPHERAL:
+ case OTG_STATE_A_WAIT_VFALL:
+ case OTG_STATE_A_VBUS_ERR:
+ dev_err(otgd->dev, "%s: otg: invalid usb-drd state: %s\n",
+ __func__, usb_otg_state_string(state));
+ drd_set_state(fsm, OTG_STATE_UNDEFINED);
+ break;
+ }
+
+ mutex_unlock(&fsm->lock);
+ dev_dbg(otgd->dev, "otg: quit statemachine, changed %d\n",
+ fsm->state_changed);
+
+ return fsm->state_changed;
+}
+
+/**
+ * OTG FSM/DRD work function
*/
static void usb_otg_work(struct work_struct *work)
{
struct usb_otg *otgd = container_of(work, struct usb_otg, work);

- otg_statemachine(&otgd->fsm);
+ /* OTG state machine */
+ if (!otgd->drd_only) {
+ otg_statemachine(&otgd->fsm);
+ return;
+ }
+
+ /* DRD state machine */
+ drd_statemachine(&otgd->fsm);
}

/**
@@ -584,13 +735,22 @@ struct otg_fsm *usb_otg_register(struct device *dev,
goto err_wq;
}

- usb_otg_init_timers(otgd, config->otg_timeouts);
+ if (!(otgd->caps->hnp_support || otgd->caps->srp_support ||
+ otgd->caps->adp_support))
+ otgd->drd_only = true;

/* create copy of original ops */
otgd->fsm_ops = *config->fsm_ops;
- /* FIXME: we ignore caller's timer ops */
- otgd->fsm_ops.add_timer = usb_otg_add_timer;
- otgd->fsm_ops.del_timer = usb_otg_del_timer;
+
+ /* For DRD mode we don't need OTG timers */
+ if (!otgd->drd_only) {
+ usb_otg_init_timers(otgd, config->otg_timeouts);
+
+ /* FIXME: we ignore caller's timer ops */
+ otgd->fsm_ops.add_timer = usb_otg_add_timer;
+ otgd->fsm_ops.del_timer = usb_otg_del_timer;
+ }
+
/* set otg ops */
otgd->fsm.ops = &otgd->fsm_ops;
otgd->fsm.otg = otgd;
@@ -703,8 +863,10 @@ static void usb_otg_stop_fsm(struct otg_fsm *fsm)
otgd->fsm_running = false;

/* Stop state machine / timers */
- for (i = 0; i < ARRAY_SIZE(otgd->timers); i++)
- hrtimer_cancel(&otgd->timers[i].timer);
+ if (!otgd->drd_only) {
+ for (i = 0; i < ARRAY_SIZE(otgd->timers); i++)
+ hrtimer_cancel(&otgd->timers[i].timer);
+ }

flush_workqueue(otgd->wq);
fsm->otg->state = OTG_STATE_UNDEFINED;
diff --git a/include/linux/usb/otg-fsm.h b/include/linux/usb/otg-fsm.h
index 75e82cc..48a6aea 100644
--- a/include/linux/usb/otg-fsm.h
+++ b/include/linux/usb/otg-fsm.h
@@ -48,6 +48,11 @@ enum otg_fsm_timer {
/**
* struct otg_fsm - OTG state machine according to the OTG spec
*
+ * DRD mode hardware Inputs
+ *
+ * @id: TRUE for B-device, FALSE for A-device.
+ * @b_sess_vld: VBUS voltage in regulation.
+ *
* OTG hardware Inputs
*
* Common inputs for A and B device
diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h
index 38cabe0..18de812 100644
--- a/include/linux/usb/otg.h
+++ b/include/linux/usb/otg.h
@@ -74,6 +74,7 @@ struct otg_timer {
* @work: otg state machine work
* @wq: otg state machine work queue
* @fsm_running: state machine running/stopped indicator
+ * @drd_only: dual-role mode. no otg features.
*/
struct usb_otg {
u8 default_a;
@@ -102,6 +103,7 @@ struct usb_otg {
struct workqueue_struct *wq;
bool fsm_running;
/* use otg->fsm.lock for serializing access */
+ bool drd_only;

/*------------- deprecated interface -----------------------------*/
/* bind/unbind the host controller */
--
2.1.4

2015-08-26 07:31:28

by Peter Chen

[permalink] [raw]
Subject: Re: [PATCH v4 00/13] USB: OTG/DRD Core functionality

On Mon, Aug 24, 2015 at 04:21:11PM +0300, Roger Quadros wrote:
> 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 platform related patches will be sent separately.
>

I will review this patch set after you send the above, it will be
more clear if there are users for code.

Peter
> Series is based on Greg's usb-next tree.
>
> Changelog:
> ---------
> 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.
>
> Why?:
> ----
>
> Most of the OTG drivers have been dealing with the OTG state machine
> themselves and there is a scope for code re-use. This has been
> partly addressed by the usb/common/usb-otg-fsm.c but it still
> leaves the instantiation of the state machine and OTG timers
> to the controller drivers. We re-use usb-otg-fsm.c but
> go one step further by instantiating the state machine and timers
> thus making it easier for drivers to implement OTG functionality.
>
> 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 in OTG mode. i.e. to stop and start them from a
> central location. This central location should be the USB OTG 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 can't be done as of now and can be done from the OTG core.
>
> What?:
> -----
>
> The OTG core instantiates the OTG/DRD Finite State Machine
> per OTG controller and manages starting/stopping the
> host and gadget controllers based on the bus state.
>
> It provides APIs for the following
>
> - Registering an OTG capable controller
> struct otg_fsm *usb_otg_register(struct device *dev,
> struct usb_otg_config *config);
>
> int usb_otg_unregister(struct device *dev);
>
> - Registering Host controllers to OTG core (used by hcd-core)
> 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);
>
>
> - Registering Gadget controllers to OTG core (used by udc-core)
> int usb_otg_register_gadget(struct usb_gadget *gadget,
> struct otg_gadget_ops *ops);
> int usb_otg_unregister_gadget(struct usb_gadget *gadget);
>
>
> - Providing inputs to and kicking the OTG state machine
> void usb_otg_sync_inputs(struct otg_fsm *fsm);
> int usb_otg_kick_fsm(struct device *hcd_gcd_device);
>
> - Getting controller device structure from OTG state machine instance
> struct device *usb_otg_fsm_to_dev(struct otg_fsm *fsm);
>
> 'struct otg_fsm' is the interface to the OTG state machine.
> It contains inputs to the fsm, status of the fsm and operations
> for the OTG controller driver.
>
> - Helper APIs for starting/stopping host/gadget controllers
> int usb_otg_start_host(struct otg_fsm *fsm, int on);
> int usb_otg_start_gadget(struct otg_fsm *fsm, int on);
>
> Usage model:
> -----------
>
> - The OTG core needs to know what host and gadget controllers are
> linked to the OTG controller. For DT boots we can provide that
> information by adding "otg-controller" property to the host and
> gadget controller nodes that points to the right otg controller.
> For legacy boot we assume that OTG controller is the parent
> of the host and gadget controllers. For DT if "otg-controller"
> property is not present then parent child relationship constraint
> applies.
>
> - The OTG controller driver must call usb_otg_register() to register
> itself with the OTG core. It must also provide the required
> OTG configuration, fsm operations and timer timeouts (optional)
> via struct usb_otg_config. The fsm operations will be called
> depending on the OTG bus state.
>
> - The host/gadget core stacks are modified to inform the OTG core
> whenever a new host/gadget device is added. The OTG core then
> checks if the host/gadget is part of the OTG controller and if yes
> then prevents the host/gadget from starting till both host and
> gadget are registered, OTG state machine is running and the
> USB bus state is appropriate to start host/gadget.
> For this, APIs have been added to host/gadget stacks to start/stop
> the controllers from the OTG core.
> For DT boots, If the OTG controller hasn't yet been registered
> while the host/gadget are added, the OTG core will hold it in a wait list
> and register them when the OTG controller registers.
>
> - No modification is needed for the host/gadget controller drivers.
> They must ensure that their start/stop methods can be called repeatedly
> and any shared resources between host & gadget are properly managed.
> The OTG core ensures that both are not started simultaneously.
>
> - The OTG core instantiates one OTG state machine per OTG controller
> and the necessary OTG timers to manage OTG state timeouts.
> If none of the otg features are set during usb_otg_register() then it
> instanciates a DRD (dual-role device) state machine instead.
> The state machine is started when both host & gadget register and
> stopped when either of them unregisters. The controllers are started
> and stopped depending on bus state.
>
> - During the lifetime of the OTG state machine, inputs can be
> provided to it by modifying the appropriate members of 'struct otg_fsm'
> and calling usb_otg_sync_inputs(). This is typically done by the
> OTG controller driver that called usb_otg_register().
>
> --
> cheers,
> -roger
>
> Roger Quadros (13):
> usb: otg-fsm: Add documentation for struct otg_fsm
> usb: otg-fsm: support multiple instances
> usb: otg-fsm: Prevent build warning "VDBG" redefined
> otg-fsm: move usb_bus_start_enum into otg-fsm->ops
> usb: hcd.h: Add OTG to HCD interface
> usb: gadget.h: Add OTG to gadget interface
> usb: otg: add OTG core
> usb: doc: dt-binding: Add otg-controller property
> usb: chipidea: move from CONFIG_USB_OTG_FSM to CONFIG_USB_OTG
> usb: hcd: Adapt to OTG core
> usb: core: hub: Notify OTG fsm when A device sets b_hnp_enable
> usb: gadget: udc: adapt to OTG core
> usb: otg: Add dual-role device (DRD) support
>
> Documentation/devicetree/bindings/usb/generic.txt | 5 +
> Documentation/usb/chipidea.txt | 2 +-
> MAINTAINERS | 4 +-
> drivers/usb/Kconfig | 2 +-
> drivers/usb/Makefile | 1 +
> drivers/usb/chipidea/Makefile | 2 +-
> drivers/usb/chipidea/ci.h | 2 +-
> drivers/usb/chipidea/otg_fsm.c | 1 +
> drivers/usb/chipidea/otg_fsm.h | 2 +-
> drivers/usb/common/Makefile | 3 +-
> drivers/usb/common/usb-otg-fsm.c | 26 +-
> drivers/usb/common/usb-otg.c | 1223 +++++++++++++++++++++
> drivers/usb/common/usb-otg.h | 71 ++
> drivers/usb/core/Kconfig | 11 +-
> drivers/usb/core/hcd.c | 55 +-
> drivers/usb/core/hub.c | 10 +-
> drivers/usb/gadget/udc/udc-core.c | 124 ++-
> drivers/usb/phy/Kconfig | 2 +-
> drivers/usb/phy/phy-fsl-usb.c | 3 +
> include/linux/usb/gadget.h | 14 +
> include/linux/usb/hcd.h | 14 +
> include/linux/usb/otg-fsm.h | 116 +-
> include/linux/usb/otg.h | 191 +++-
> 23 files changed, 1808 insertions(+), 76 deletions(-)
> create mode 100644 drivers/usb/common/usb-otg.c
> create mode 100644 drivers/usb/common/usb-otg.h
>
> --
> 2.1.4
>

--

Best Regards,
Peter Chen

2015-12-03 08:23:33

by Peter Chen

[permalink] [raw]
Subject: Re: [PATCH v4 00/13] USB: OTG/DRD Core functionality

On Mon, Aug 24, 2015 at 04:21:11PM +0300, Roger Quadros wrote:
> 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 platform related patches will be sent separately.
>
> Series is based on Greg's usb-next tree.

Hi Roger,

Will you go on for this patch set?

Peter
>
> Changelog:
> ---------
> 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.
>
> Why?:
> ----
>
> Most of the OTG drivers have been dealing with the OTG state machine
> themselves and there is a scope for code re-use. This has been
> partly addressed by the usb/common/usb-otg-fsm.c but it still
> leaves the instantiation of the state machine and OTG timers
> to the controller drivers. We re-use usb-otg-fsm.c but
> go one step further by instantiating the state machine and timers
> thus making it easier for drivers to implement OTG functionality.
>
> 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 in OTG mode. i.e. to stop and start them from a
> central location. This central location should be the USB OTG 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 can't be done as of now and can be done from the OTG core.
>
> What?:
> -----
>
> The OTG core instantiates the OTG/DRD Finite State Machine
> per OTG controller and manages starting/stopping the
> host and gadget controllers based on the bus state.
>
> It provides APIs for the following
>
> - Registering an OTG capable controller
> struct otg_fsm *usb_otg_register(struct device *dev,
> struct usb_otg_config *config);
>
> int usb_otg_unregister(struct device *dev);
>
> - Registering Host controllers to OTG core (used by hcd-core)
> 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);
>
>
> - Registering Gadget controllers to OTG core (used by udc-core)
> int usb_otg_register_gadget(struct usb_gadget *gadget,
> struct otg_gadget_ops *ops);
> int usb_otg_unregister_gadget(struct usb_gadget *gadget);
>
>
> - Providing inputs to and kicking the OTG state machine
> void usb_otg_sync_inputs(struct otg_fsm *fsm);
> int usb_otg_kick_fsm(struct device *hcd_gcd_device);
>
> - Getting controller device structure from OTG state machine instance
> struct device *usb_otg_fsm_to_dev(struct otg_fsm *fsm);
>
> 'struct otg_fsm' is the interface to the OTG state machine.
> It contains inputs to the fsm, status of the fsm and operations
> for the OTG controller driver.
>
> - Helper APIs for starting/stopping host/gadget controllers
> int usb_otg_start_host(struct otg_fsm *fsm, int on);
> int usb_otg_start_gadget(struct otg_fsm *fsm, int on);
>
> Usage model:
> -----------
>
> - The OTG core needs to know what host and gadget controllers are
> linked to the OTG controller. For DT boots we can provide that
> information by adding "otg-controller" property to the host and
> gadget controller nodes that points to the right otg controller.
> For legacy boot we assume that OTG controller is the parent
> of the host and gadget controllers. For DT if "otg-controller"
> property is not present then parent child relationship constraint
> applies.
>
> - The OTG controller driver must call usb_otg_register() to register
> itself with the OTG core. It must also provide the required
> OTG configuration, fsm operations and timer timeouts (optional)
> via struct usb_otg_config. The fsm operations will be called
> depending on the OTG bus state.
>
> - The host/gadget core stacks are modified to inform the OTG core
> whenever a new host/gadget device is added. The OTG core then
> checks if the host/gadget is part of the OTG controller and if yes
> then prevents the host/gadget from starting till both host and
> gadget are registered, OTG state machine is running and the
> USB bus state is appropriate to start host/gadget.
> For this, APIs have been added to host/gadget stacks to start/stop
> the controllers from the OTG core.
> For DT boots, If the OTG controller hasn't yet been registered
> while the host/gadget are added, the OTG core will hold it in a wait list
> and register them when the OTG controller registers.
>
> - No modification is needed for the host/gadget controller drivers.
> They must ensure that their start/stop methods can be called repeatedly
> and any shared resources between host & gadget are properly managed.
> The OTG core ensures that both are not started simultaneously.
>
> - The OTG core instantiates one OTG state machine per OTG controller
> and the necessary OTG timers to manage OTG state timeouts.
> If none of the otg features are set during usb_otg_register() then it
> instanciates a DRD (dual-role device) state machine instead.
> The state machine is started when both host & gadget register and
> stopped when either of them unregisters. The controllers are started
> and stopped depending on bus state.
>
> - During the lifetime of the OTG state machine, inputs can be
> provided to it by modifying the appropriate members of 'struct otg_fsm'
> and calling usb_otg_sync_inputs(). This is typically done by the
> OTG controller driver that called usb_otg_register().
>
> --
> cheers,
> -roger
>
> Roger Quadros (13):
> usb: otg-fsm: Add documentation for struct otg_fsm
> usb: otg-fsm: support multiple instances
> usb: otg-fsm: Prevent build warning "VDBG" redefined
> otg-fsm: move usb_bus_start_enum into otg-fsm->ops
> usb: hcd.h: Add OTG to HCD interface
> usb: gadget.h: Add OTG to gadget interface
> usb: otg: add OTG core
> usb: doc: dt-binding: Add otg-controller property
> usb: chipidea: move from CONFIG_USB_OTG_FSM to CONFIG_USB_OTG
> usb: hcd: Adapt to OTG core
> usb: core: hub: Notify OTG fsm when A device sets b_hnp_enable
> usb: gadget: udc: adapt to OTG core
> usb: otg: Add dual-role device (DRD) support
>
> Documentation/devicetree/bindings/usb/generic.txt | 5 +
> Documentation/usb/chipidea.txt | 2 +-
> MAINTAINERS | 4 +-
> drivers/usb/Kconfig | 2 +-
> drivers/usb/Makefile | 1 +
> drivers/usb/chipidea/Makefile | 2 +-
> drivers/usb/chipidea/ci.h | 2 +-
> drivers/usb/chipidea/otg_fsm.c | 1 +
> drivers/usb/chipidea/otg_fsm.h | 2 +-
> drivers/usb/common/Makefile | 3 +-
> drivers/usb/common/usb-otg-fsm.c | 26 +-
> drivers/usb/common/usb-otg.c | 1223 +++++++++++++++++++++
> drivers/usb/common/usb-otg.h | 71 ++
> drivers/usb/core/Kconfig | 11 +-
> drivers/usb/core/hcd.c | 55 +-
> drivers/usb/core/hub.c | 10 +-
> drivers/usb/gadget/udc/udc-core.c | 124 ++-
> drivers/usb/phy/Kconfig | 2 +-
> drivers/usb/phy/phy-fsl-usb.c | 3 +
> include/linux/usb/gadget.h | 14 +
> include/linux/usb/hcd.h | 14 +
> include/linux/usb/otg-fsm.h | 116 +-
> include/linux/usb/otg.h | 191 +++-
> 23 files changed, 1808 insertions(+), 76 deletions(-)
> create mode 100644 drivers/usb/common/usb-otg.c
> create mode 100644 drivers/usb/common/usb-otg.h
>
> --
> 2.1.4
>

--

Best Regards,
Peter Chen

2015-12-03 08:54:29

by Roger Quadros

[permalink] [raw]
Subject: Re: [PATCH v4 00/13] USB: OTG/DRD Core functionality

Peter,

On 03/12/15 13:49, Peter Chen wrote:
> On Mon, Aug 24, 2015 at 04:21:11PM +0300, Roger Quadros wrote:
>> 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 platform related patches will be sent separately.
>>
>> Series is based on Greg's usb-next tree.
>
> Hi Roger,
>
> Will you go on for this patch set?

Yes, but I can only work on this from Feb onwards.

cheers,
-roger
>
> Peter
>>
>> Changelog:
>> ---------
>> 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.
>>
>> Why?:
>> ----
>>
>> Most of the OTG drivers have been dealing with the OTG state machine
>> themselves and there is a scope for code re-use. This has been
>> partly addressed by the usb/common/usb-otg-fsm.c but it still
>> leaves the instantiation of the state machine and OTG timers
>> to the controller drivers. We re-use usb-otg-fsm.c but
>> go one step further by instantiating the state machine and timers
>> thus making it easier for drivers to implement OTG functionality.
>>
>> 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 in OTG mode. i.e. to stop and start them from a
>> central location. This central location should be the USB OTG 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 can't be done as of now and can be done from the OTG core.
>>
>> What?:
>> -----
>>
>> The OTG core instantiates the OTG/DRD Finite State Machine
>> per OTG controller and manages starting/stopping the
>> host and gadget controllers based on the bus state.
>>
>> It provides APIs for the following
>>
>> - Registering an OTG capable controller
>> struct otg_fsm *usb_otg_register(struct device *dev,
>> struct usb_otg_config *config);
>>
>> int usb_otg_unregister(struct device *dev);
>>
>> - Registering Host controllers to OTG core (used by hcd-core)
>> 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);
>>
>>
>> - Registering Gadget controllers to OTG core (used by udc-core)
>> int usb_otg_register_gadget(struct usb_gadget *gadget,
>> struct otg_gadget_ops *ops);
>> int usb_otg_unregister_gadget(struct usb_gadget *gadget);
>>
>>
>> - Providing inputs to and kicking the OTG state machine
>> void usb_otg_sync_inputs(struct otg_fsm *fsm);
>> int usb_otg_kick_fsm(struct device *hcd_gcd_device);
>>
>> - Getting controller device structure from OTG state machine instance
>> struct device *usb_otg_fsm_to_dev(struct otg_fsm *fsm);
>>
>> 'struct otg_fsm' is the interface to the OTG state machine.
>> It contains inputs to the fsm, status of the fsm and operations
>> for the OTG controller driver.
>>
>> - Helper APIs for starting/stopping host/gadget controllers
>> int usb_otg_start_host(struct otg_fsm *fsm, int on);
>> int usb_otg_start_gadget(struct otg_fsm *fsm, int on);
>>
>> Usage model:
>> -----------
>>
>> - The OTG core needs to know what host and gadget controllers are
>> linked to the OTG controller. For DT boots we can provide that
>> information by adding "otg-controller" property to the host and
>> gadget controller nodes that points to the right otg controller.
>> For legacy boot we assume that OTG controller is the parent
>> of the host and gadget controllers. For DT if "otg-controller"
>> property is not present then parent child relationship constraint
>> applies.
>>
>> - The OTG controller driver must call usb_otg_register() to register
>> itself with the OTG core. It must also provide the required
>> OTG configuration, fsm operations and timer timeouts (optional)
>> via struct usb_otg_config. The fsm operations will be called
>> depending on the OTG bus state.
>>
>> - The host/gadget core stacks are modified to inform the OTG core
>> whenever a new host/gadget device is added. The OTG core then
>> checks if the host/gadget is part of the OTG controller and if yes
>> then prevents the host/gadget from starting till both host and
>> gadget are registered, OTG state machine is running and the
>> USB bus state is appropriate to start host/gadget.
>> For this, APIs have been added to host/gadget stacks to start/stop
>> the controllers from the OTG core.
>> For DT boots, If the OTG controller hasn't yet been registered
>> while the host/gadget are added, the OTG core will hold it in a wait list
>> and register them when the OTG controller registers.
>>
>> - No modification is needed for the host/gadget controller drivers.
>> They must ensure that their start/stop methods can be called repeatedly
>> and any shared resources between host & gadget are properly managed.
>> The OTG core ensures that both are not started simultaneously.
>>
>> - The OTG core instantiates one OTG state machine per OTG controller
>> and the necessary OTG timers to manage OTG state timeouts.
>> If none of the otg features are set during usb_otg_register() then it
>> instanciates a DRD (dual-role device) state machine instead.
>> The state machine is started when both host & gadget register and
>> stopped when either of them unregisters. The controllers are started
>> and stopped depending on bus state.
>>
>> - During the lifetime of the OTG state machine, inputs can be
>> provided to it by modifying the appropriate members of 'struct otg_fsm'
>> and calling usb_otg_sync_inputs(). This is typically done by the
>> OTG controller driver that called usb_otg_register().
>>
>> --
>> cheers,
>> -roger
>>
>> Roger Quadros (13):
>> usb: otg-fsm: Add documentation for struct otg_fsm
>> usb: otg-fsm: support multiple instances
>> usb: otg-fsm: Prevent build warning "VDBG" redefined
>> otg-fsm: move usb_bus_start_enum into otg-fsm->ops
>> usb: hcd.h: Add OTG to HCD interface
>> usb: gadget.h: Add OTG to gadget interface
>> usb: otg: add OTG core
>> usb: doc: dt-binding: Add otg-controller property
>> usb: chipidea: move from CONFIG_USB_OTG_FSM to CONFIG_USB_OTG
>> usb: hcd: Adapt to OTG core
>> usb: core: hub: Notify OTG fsm when A device sets b_hnp_enable
>> usb: gadget: udc: adapt to OTG core
>> usb: otg: Add dual-role device (DRD) support
>>
>> Documentation/devicetree/bindings/usb/generic.txt | 5 +
>> Documentation/usb/chipidea.txt | 2 +-
>> MAINTAINERS | 4 +-
>> drivers/usb/Kconfig | 2 +-
>> drivers/usb/Makefile | 1 +
>> drivers/usb/chipidea/Makefile | 2 +-
>> drivers/usb/chipidea/ci.h | 2 +-
>> drivers/usb/chipidea/otg_fsm.c | 1 +
>> drivers/usb/chipidea/otg_fsm.h | 2 +-
>> drivers/usb/common/Makefile | 3 +-
>> drivers/usb/common/usb-otg-fsm.c | 26 +-
>> drivers/usb/common/usb-otg.c | 1223 +++++++++++++++++++++
>> drivers/usb/common/usb-otg.h | 71 ++
>> drivers/usb/core/Kconfig | 11 +-
>> drivers/usb/core/hcd.c | 55 +-
>> drivers/usb/core/hub.c | 10 +-
>> drivers/usb/gadget/udc/udc-core.c | 124 ++-
>> drivers/usb/phy/Kconfig | 2 +-
>> drivers/usb/phy/phy-fsl-usb.c | 3 +
>> include/linux/usb/gadget.h | 14 +
>> include/linux/usb/hcd.h | 14 +
>> include/linux/usb/otg-fsm.h | 116 +-
>> include/linux/usb/otg.h | 191 +++-
>> 23 files changed, 1808 insertions(+), 76 deletions(-)
>> create mode 100644 drivers/usb/common/usb-otg.c
>> create mode 100644 drivers/usb/common/usb-otg.h
>>
>> --
>> 2.1.4
>>
>