2015-07-08 10:19:58

by Roger Quadros

[permalink] [raw]
Subject: [PATCH v3 00/11] 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. xhci side of things for OTG/DRD use are already
in v4.2

http://thread.gmane.org/gmane.linux.kernel/1923161

DWC3 controller and platform related patches are sent separately.

Changelog:
---------
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 *parent_dev,
struct otg_fsm_ops *fsm_ops,
bool drd_only);
int usb_otg_unregister(struct device *parent_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.

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

- The OTG controller device is assumed to be the parent of
the host and gadget controller. It must call usb_otg_register()
before populating the host and gadget devices so that the OTG
core is aware that it is an OTG device before the host & gadget
register. The OTG controller must provide struct otg_fsm_ops *
which will be called by the OTG core depending on 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.

- 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 drd_only parameter is 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() since it is
the only external component that has the 'struct otg_fsm' handle.

--
cheers,
-roger

Roger Quadros (11):
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: hcd: Adapt to OTG core
usb: gadget: udc: adapt to OTG core
usb: otg: Add dual-role device (DRD) support
usb: otg: hub: Notify OTG fsm when A device sets b_hnp_enable

MAINTAINERS | 4 +-
drivers/usb/Kconfig | 2 +-
drivers/usb/Makefile | 1 +
drivers/usb/chipidea/otg_fsm.c | 1 +
drivers/usb/common/Makefile | 3 +-
drivers/usb/common/usb-otg-fsm.c | 26 +-
drivers/usb/common/usb-otg.c | 929 ++++++++++++++++++++++++++++++++++++++
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/phy-fsl-usb.c | 3 +
include/linux/usb/gadget.h | 14 +
include/linux/usb/hcd.h | 14 +
include/linux/usb/otg-fsm.h | 118 ++++-
include/linux/usb/otg.h | 92 +++-
17 files changed, 1406 insertions(+), 72 deletions(-)
create mode 100644 drivers/usb/common/usb-otg.c
create mode 100644 drivers/usb/common/usb-otg.h

--
2.1.4


2015-07-08 10:20:12

by Roger Quadros

[permalink] [raw]
Subject: [PATCH v3 01/11] 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]>
---
include/linux/usb/otg-fsm.h | 89 +++++++++++++++++++++++++++++++++++++++++----
1 file changed, 82 insertions(+), 7 deletions(-)

diff --git a/include/linux/usb/otg-fsm.h b/include/linux/usb/otg-fsm.h
index f728f18..ca508c2 100644
--- a/include/linux/usb/otg-fsm.h
+++ b/include/linux/usb/otg-fsm.h
@@ -59,37 +59,112 @@ 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_ops() helpers
+ *
+ * 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 +172,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-07-08 10:20:07

by Roger Quadros

[permalink] [raw]
Subject: [PATCH v3 02/11] 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..42c6376 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,7 @@ 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;
+ fsm->state_changed = 1;
if (fsm->otg->state == new_state)
return 0;
VDBG("Set state: %s\n", usb_otg_state_string(new_state));
@@ -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 ca508c2..243274f 100644
--- a/include/linux/usb/otg-fsm.h
+++ b/include/linux/usb/otg-fsm.h
@@ -194,6 +194,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-07-08 10:23:03

by Roger Quadros

[permalink] [raw]
Subject: [PATCH v3 03/11] 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]>
---
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 19d655a..6e67f94 100644
--- a/drivers/usb/chipidea/otg_fsm.c
+++ b/drivers/usb/chipidea/otg_fsm.c
@@ -777,6 +777,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 42c6376..1873eb3 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);
@@ -124,7 +125,7 @@ static int otg_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state)
fsm->state_changed = 1;
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 243274f..c631dde 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)
@@ -195,6 +181,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-07-08 10:20:14

by Roger Quadros

[permalink] [raw]
Subject: [PATCH v3 04/11] 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]>
---
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 1873eb3..156fd25 100644
--- a/drivers/usb/common/usb-otg-fsm.c
+++ b/drivers/usb/common/usb-otg-fsm.c
@@ -166,8 +166,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 c631dde..22d8baa 100644
--- a/include/linux/usb/otg-fsm.h
+++ b/include/linux/usb/otg-fsm.h
@@ -198,6 +198,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-07-08 10:22:42

by Roger Quadros

[permalink] [raw]
Subject: [PATCH v3 05/11] 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]>
---
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 c9aa779..4108288 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-07-08 10:22:17

by Roger Quadros

[permalink] [raw]
Subject: [PATCH v3 06/11] 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]>
---
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 4f3dfb7..0b4b341 100644
--- a/include/linux/usb/gadget.h
+++ b/include/linux/usb/gadget.h
@@ -887,6 +887,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-07-08 10:20:24

by Roger Quadros

[permalink] [raw]
Subject: [PATCH v3 07/11] 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 | 768 +++++++++++++++++++++++++++++++++++++++++++
drivers/usb/common/usb-otg.h | 71 ++++
drivers/usb/core/Kconfig | 11 +-
include/linux/usb/otg.h | 91 ++++-
8 files changed, 930 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 8133cef..b21278e 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -10640,12 +10640,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..0379034
--- /dev/null
+++ b/drivers/usb/common/usb-otg.c
@@ -0,0 +1,768 @@
+/**
+ * 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/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"
+
+/* to link timer with callback data */
+struct otg_timer {
+ struct hrtimer timer;
+ ktime_t timeout;
+ /* callback data */
+ int *timeout_bit;
+ struct otg_data *otgd;
+};
+
+struct otg_hcd {
+ struct usb_hcd *hcd;
+ unsigned int irqnum;
+ unsigned long irqflags;
+ struct otg_hcd_ops *ops;
+};
+
+struct otg_data {
+ struct device *dev; /* HCD & GCD's parent device */
+
+ struct otg_fsm fsm;
+ /* HCD, GCD and usb_otg_state are present in otg_fsm->otg
+ * HCD is bus_to_hcd(fsm->otg->host)
+ * GCD is fsm->otg->gadget
+ */
+ struct otg_fsm_ops fsm_ops; /* private copy for override */
+ struct usb_otg otg; /* allocator for fsm->otg */
+
+ struct otg_hcd primary_hcd;
+ struct otg_hcd shared_hcd;
+
+ struct otg_gadget_ops *gadget_ops; /* interface to gadget f/w */
+
+ /* saved hooks to OTG device */
+ int (*start_host)(struct otg_fsm *fsm, int on);
+ int (*start_gadget)(struct otg_fsm *fsm, int on);
+
+ struct list_head list;
+
+ struct work_struct work; /* OTG FSM work */
+ struct workqueue_struct *wq;
+
+ struct otg_timer timers[NUM_OTG_FSM_TIMERS];
+
+ bool fsm_running;
+ /* use otg->fsm.lock for serializing access */
+};
+
+/* OTG device list */
+LIST_HEAD(otg_list);
+static DEFINE_MUTEX(otg_list_mutex);
+
+static int usb_otg_hcd_is_primary_hcd(struct usb_hcd *hcd)
+{
+ if (!hcd->primary_hcd)
+ return 1;
+ return hcd == hcd->primary_hcd;
+}
+
+/**
+ * check if device is in our OTG list and return
+ * otg_data, else NULL.
+ *
+ * otg_list_mutex must be held.
+ */
+static struct otg_data *usb_otg_device_get_otgd(struct device *parent_dev)
+{
+ struct otg_data *otgd;
+
+ list_for_each_entry(otgd, &otg_list, list) {
+ if (otgd->dev == parent_dev)
+ return otgd;
+ }
+
+ return NULL;
+}
+
+/**
+ * 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 otg_data *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 otg_data *otgd)
+{
+ struct otg_fsm *fsm = &otgd->fsm;
+
+ 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);
+
+ otg_timer_init(A_WAIT_ENUM, otgd, set_tmout, TB_SRP_FAIL, NULL);
+}
+
+/**
+ * OTG FSM ops function to add timer
+ */
+static void usb_otg_add_timer(struct otg_fsm *fsm, enum otg_fsm_timer id)
+{
+ struct otg_data *otgd = container_of(fsm, struct otg_data, 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 otg_data *otgd = container_of(fsm, struct otg_data, fsm);
+ struct hrtimer *timer = &otgd->timers[id].timer;
+
+ hrtimer_cancel(timer);
+}
+
+/**
+ * OTG FSM ops function to start/stop host
+ */
+static int usb_otg_start_host(struct otg_fsm *fsm, int on)
+{
+ struct otg_data *otgd = container_of(fsm, struct otg_data, 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) {
+ /* OTG device operations */
+ if (otgd->start_host)
+ otgd->start_host(fsm, 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);
+
+ /* OTG device operations */
+ if (otgd->start_host)
+ otgd->start_host(fsm, on);
+ }
+
+ return 0;
+}
+
+/**
+ * OTG FSM ops function to start/stop gadget
+ */
+static int usb_otg_start_gadget(struct otg_fsm *fsm, int on)
+{
+ struct otg_data *otgd = container_of(fsm, struct otg_data, 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) {
+ /* OTG device operations */
+ if (otgd->start_gadget)
+ otgd->start_gadget(fsm, on);
+
+ otgd->gadget_ops->start(fsm->otg->gadget);
+ } else {
+ otgd->gadget_ops->stop(fsm->otg->gadget);
+
+ /* OTG device operations */
+ if (otgd->start_gadget)
+ otgd->start_gadget(fsm, on);
+ }
+
+ return 0;
+}
+
+/**
+ * OTG FSM work function
+ */
+static void usb_otg_work(struct work_struct *work)
+{
+ struct otg_data *otgd = container_of(work, struct otg_data, work);
+
+ otg_statemachine(&otgd->fsm);
+}
+
+/**
+ * usb_otg_register() - Register the OTG device to OTG core
+ * @parent_device: parent device of Host & Gadget controllers.
+ * @otg_fsm_ops: otg state machine ops.
+ *
+ * Register parent device that contains both HCD and GCD into
+ * the USB OTG core. HCD and GCD will be prevented from starting
+ * till both are available for use.
+ *
+ * Return: struct otg_fsm * if success, NULL if error.
+ */
+struct otg_fsm *usb_otg_register(struct device *parent_dev,
+ struct otg_fsm_ops *fsm_ops)
+{
+ struct otg_data *otgd;
+ int ret = 0;
+
+ if (!parent_dev || !fsm_ops)
+ return ERR_PTR(-EINVAL);
+
+ /* already in list? */
+ mutex_lock(&otg_list_mutex);
+ if (usb_otg_device_get_otgd(parent_dev)) {
+ dev_err(parent_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 = parent_dev;
+ INIT_WORK(&otgd->work, usb_otg_work);
+ otgd->wq = create_singlethread_workqueue("usb_otg");
+ if (!otgd->wq) {
+ dev_err(parent_dev, "otg: %s: can't create workqueue\n",
+ __func__);
+ ret = -ENODEV;
+ goto err_wq;
+ }
+
+ usb_otg_init_timers(otgd);
+
+ /* save original start host/gadget ops */
+ otgd->start_host = fsm_ops->start_host;
+ otgd->start_gadget = fsm_ops->start_gadget;
+ /* create copy of original ops */
+ otgd->fsm_ops = *fsm_ops;
+ /* override ops */
+ otgd->fsm_ops.start_host = usb_otg_start_host;
+ otgd->fsm_ops.start_gadget = usb_otg_start_gadget;
+ /* FIXME: we ignore caller's timer ops */
+ otgd->fsm_ops.add_timer = usb_otg_add_timer;
+ otgd->fsm_ops.del_timer = usb_otg_del_timer;
+ /* set otg ops */
+ otgd->fsm.ops = &otgd->fsm_ops;
+ otgd->fsm.otg = &otgd->otg;
+
+ mutex_init(&otgd->fsm.lock);
+
+ list_add_tail(&otgd->list, &otg_list);
+ mutex_unlock(&otg_list_mutex);
+ 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
+ * @parent_device: parent device of Host & Gadget controllers.
+ *
+ * Unregister parent OTG deviced from USB OTG core.
+ * Prevents unregistering till both Host and Gadget controllers
+ * have unregistered from the OTG core.
+ *
+ * Return: 0 on success, error value otherwise.
+ */
+int usb_otg_unregister(struct device *parent_dev)
+{
+ struct otg_data *otgd;
+
+ mutex_lock(&otg_list_mutex);
+ otgd = usb_otg_device_get_otgd(parent_dev);
+ if (!otgd) {
+ dev_err(parent_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(parent_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 otg_data *otgd = container_of(fsm, struct otg_data, 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 otg_data *otgd = container_of(fsm, struct otg_data, 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 otg_data *otgd = container_of(fsm, struct otg_data, 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 otg_data *otgd;
+
+ mutex_lock(&otg_list_mutex);
+ otgd = usb_otg_device_get_otgd(hcd_gcd_device->parent);
+ if (!otgd) {
+ dev_err(hcd_gcd_device, "otg: %s: invalid host/gadget device\n",
+ __func__);
+ mutex_unlock(&otg_list_mutex);
+ return -ENODEV;
+ }
+
+ mutex_unlock(&otg_list_mutex);
+ 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 otg_data *otgd;
+ struct device *otg_dev = hcd->self.controller->parent;
+
+ mutex_lock(&otg_list_mutex);
+ otgd = usb_otg_device_get_otgd(otg_dev);
+ if (!otgd) {
+ dev_dbg(otg_dev, "otg: %s: device not registered to otg core\n",
+ __func__);
+ mutex_unlock(&otg_list_mutex);
+ return -EINVAL;
+ }
+
+ mutex_unlock(&otg_list_mutex);
+ /* 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 otg_data *otgd;
+ struct usb_bus *bus = hcd_to_bus(hcd);
+ struct device *otg_dev = bus->controller->parent;
+
+ mutex_lock(&otg_list_mutex);
+ otgd = usb_otg_device_get_otgd(otg_dev);
+ if (!otgd) {
+ dev_err(otg_dev, "otg: %s: device not registered to otg core\n",
+ __func__);
+ mutex_unlock(&otg_list_mutex);
+ return -EINVAL;
+ }
+
+ mutex_unlock(&otg_list_mutex);
+
+ 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(bus->controller));
+ } else if (hcd == otgd->shared_hcd.hcd) {
+ otgd->shared_hcd.hcd = NULL;
+ dev_info(otg_dev, "otg: shared host %s unregistered\n",
+ dev_name(bus->controller));
+ } else {
+ dev_err(otg_dev, "otg: host %s wasn't registered with otg\n",
+ dev_name(bus->controller));
+ 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 otg_data *otgd;
+ struct device *otg_dev = gadget->dev.parent;
+
+ mutex_lock(&otg_list_mutex);
+ otgd = usb_otg_device_get_otgd(otg_dev);
+ if (!otgd) {
+ dev_err(otg_dev, "otg: %s: device not registered to otg core\n",
+ __func__);
+ mutex_unlock(&otg_list_mutex);
+ return -EINVAL;
+ }
+
+ mutex_unlock(&otg_list_mutex);
+
+ 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 otg_data *otgd;
+ struct device *otg_dev = gadget->dev.parent;
+
+ mutex_lock(&otg_list_mutex);
+ otgd = usb_otg_device_get_otgd(otg_dev);
+ if (!otgd) {
+ dev_err(otg_dev, "otg: %s: device not registered to otg core\n",
+ __func__);
+ mutex_unlock(&otg_list_mutex);
+ return -EINVAL;
+ }
+
+ mutex_unlock(&otg_list_mutex);
+
+ 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 otg_data *otgd = container_of(fsm, struct otg_data, 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 52661c5..ce6f8d8 100644
--- a/include/linux/usb/otg.h
+++ b/include/linux/usb/otg.h
@@ -10,6 +10,11 @@
#define __LINUX_USB_OTG_H

#include <linux/phy/phy.h>
+#include <linux/device.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/otg-fsm.h>
#include <linux/usb/phy.h>

struct usb_otg {
@@ -23,6 +28,7 @@ struct usb_otg {

enum usb_otg_state state;

+/*------------- deprecated interface -----------------------------*/
/* bind/unbind the host controller */
int (*set_host)(struct usb_otg *otg, struct usb_bus *host);

@@ -38,11 +44,85 @@ struct usb_otg {

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

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 *parent_dev,
+ struct otg_fsm_ops *fsm_ops);
+int usb_otg_unregister(struct device *parent_dev);
+int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
+ unsigned long irqflags, struct otg_hcd_ops *ops);
+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);
+
+#else /* CONFIG_USB_OTG */
+
+static inline struct otg_fsm *usb_otg_register(struct device *parent_dev,
+ struct otg_fsm_ops *fsm_ops)
+{
+ return ERR_PTR(-ENOSYS);
+}
+
+static inline int usb_otg_unregister(struct device *parent_dev)
+{
+ return -ENOSYS;
+}
+
+static inline int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
+ unsigned long irqflags,
+ struct otg_hcd_ops *ops)
+{
+ return -ENOSYS;
+}
+
+static inline int usb_otg_unregister_hcd(struct usb_hcd *hcd)
+{
+ return -ENOSYS;
+}
+
+static inline int usb_otg_register_gadget(struct usb_gadget *gadget,
+ struct otg_gadget_ops *ops)
+{
+ return -ENOSYS;
+}
+
+static inline int usb_otg_unregister_gadget(struct usb_gadget *gadget)
+{
+ return -ENOSYS;
+}
+
+static inline void usb_otg_sync_inputs(struct otg_fsm *fsm)
+{
+}
+
+static inline int usb_otg_kick_fsm(struct device *hcd_gcd_device)
+{
+ return -ENOSYS;
+}
+
+static inline struct device *usb_otg_fsm_to_dev(struct otg_fsm *fsm)
+{
+ return NULL;
+}
+
+#endif /* CONFIG_USB_OTG */
+
+/*------------- deprecated interface -----------------------------*/
/* Context: can sleep */
static inline int
otg_start_hnp(struct usb_otg *otg)
@@ -94,14 +174,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-07-08 10:20:44

by Roger Quadros

[permalink] [raw]
Subject: [PATCH v3 08/11] 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/common/usb-otg.c | 6 ++---
drivers/usb/core/hcd.c | 55 ++++++++++++++++++++++++++++++++++++++++----
2 files changed, 53 insertions(+), 8 deletions(-)

diff --git a/drivers/usb/common/usb-otg.c b/drivers/usb/common/usb-otg.c
index 0379034..1f19001 100644
--- a/drivers/usb/common/usb-otg.c
+++ b/drivers/usb/common/usb-otg.c
@@ -496,7 +496,7 @@ int usb_otg_kick_fsm(struct device *hcd_gcd_device)
mutex_lock(&otg_list_mutex);
otgd = usb_otg_device_get_otgd(hcd_gcd_device->parent);
if (!otgd) {
- dev_err(hcd_gcd_device, "otg: %s: invalid host/gadget device\n",
+ dev_dbg(hcd_gcd_device, "otg: %s: invalid host/gadget device\n",
__func__);
mutex_unlock(&otg_list_mutex);
return -ENODEV;
@@ -678,7 +678,7 @@ int usb_otg_register_gadget(struct usb_gadget *gadget,
mutex_lock(&otg_list_mutex);
otgd = usb_otg_device_get_otgd(otg_dev);
if (!otgd) {
- dev_err(otg_dev, "otg: %s: device not registered to otg core\n",
+ dev_dbg(otg_dev, "otg: %s: device not registered to otg core\n",
__func__);
mutex_unlock(&otg_list_mutex);
return -EINVAL;
@@ -724,7 +724,7 @@ int usb_otg_unregister_gadget(struct usb_gadget *gadget)
mutex_lock(&otg_list_mutex);
otgd = usb_otg_device_get_otgd(otg_dev);
if (!otgd) {
- dev_err(otg_dev, "otg: %s: device not registered to otg core\n",
+ dev_dbg(otg_dev, "otg: %s: device not registered to otg core\n",
__func__);
mutex_unlock(&otg_list_mutex);
return -EINVAL;
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index be5b207..3a19607 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"

@@ -2622,8 +2623,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;
@@ -2828,17 +2829,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;

@@ -2912,6 +2912,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-07-08 10:20:41

by Roger Quadros

[permalink] [raw]
Subject: [PATCH v3 09/11] 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 d69c355..3380d03 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;
@@ -208,6 +211,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);
}

@@ -225,10 +229,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
*
@@ -345,6 +420,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",
@@ -352,10 +428,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;
@@ -380,11 +464,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);
@@ -394,6 +479,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;
@@ -408,12 +499,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;
@@ -525,9 +623,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-07-08 10:21:43

by Roger Quadros

[permalink] [raw]
Subject: [PATCH v3 10/11] 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 'vbus; that are still passed via the origintal struct otg_fsm.

Most of the usb-otg.c functionality remains the same except
adding a new parameter to usb_otg_register() to indicate that
the OTG controller needs to operate in DRD mode.

Signed-off-by: Roger Quadros <[email protected]>
---
drivers/usb/common/usb-otg.c | 179 ++++++++++++++++++++++++++++++++++++++++---
include/linux/usb/otg-fsm.h | 8 +-
include/linux/usb/otg.h | 5 +-
3 files changed, 180 insertions(+), 12 deletions(-)

diff --git a/drivers/usb/common/usb-otg.c b/drivers/usb/common/usb-otg.c
index 1f19001..9b89f4b 100644
--- a/drivers/usb/common/usb-otg.c
+++ b/drivers/usb/common/usb-otg.c
@@ -44,6 +44,7 @@ struct otg_hcd {
struct otg_data {
struct device *dev; /* HCD & GCD's parent device */

+ bool drd_only; /* Dual-role only, no OTG features */
struct otg_fsm fsm;
/* HCD, GCD and usb_otg_state are present in otg_fsm->otg
* HCD is bus_to_hcd(fsm->otg->host)
@@ -272,20 +273,172 @@ static int usb_otg_start_gadget(struct otg_fsm *fsm, int on)
return 0;
}

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

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

/**
* usb_otg_register() - Register the OTG device to OTG core
* @parent_device: parent device of Host & Gadget controllers.
* @otg_fsm_ops: otg state machine ops.
+ * @drd_only: dual-role only. no OTG features.
*
* Register parent device that contains both HCD and GCD into
* the USB OTG core. HCD and GCD will be prevented from starting
@@ -294,7 +447,8 @@ static void usb_otg_work(struct work_struct *work)
* Return: struct otg_fsm * if success, NULL if error.
*/
struct otg_fsm *usb_otg_register(struct device *parent_dev,
- struct otg_fsm_ops *fsm_ops)
+ struct otg_fsm_ops *fsm_ops,
+ bool drd_only)
{
struct otg_data *otgd;
int ret = 0;
@@ -328,7 +482,15 @@ struct otg_fsm *usb_otg_register(struct device *parent_dev,
goto err_wq;
}

- usb_otg_init_timers(otgd);
+ otgd->drd_only = drd_only;
+ /* For DRD mode we don't need OTG timers */
+ if (!drd_only) {
+ usb_otg_init_timers(otgd);
+
+ /* FIXME: we ignore caller's timer ops */
+ otgd->fsm_ops.add_timer = usb_otg_add_timer;
+ otgd->fsm_ops.del_timer = usb_otg_del_timer;
+ }

/* save original start host/gadget ops */
otgd->start_host = fsm_ops->start_host;
@@ -338,9 +500,6 @@ struct otg_fsm *usb_otg_register(struct device *parent_dev,
/* override ops */
otgd->fsm_ops.start_host = usb_otg_start_host;
otgd->fsm_ops.start_gadget = usb_otg_start_gadget;
- /* FIXME: we ignore caller's timer ops */
- otgd->fsm_ops.add_timer = usb_otg_add_timer;
- otgd->fsm_ops.del_timer = usb_otg_del_timer;
/* set otg ops */
otgd->fsm.ops = &otgd->fsm_ops;
otgd->fsm.otg = &otgd->otg;
@@ -443,8 +602,10 @@ static void usb_otg_stop_fsm(struct otg_fsm *fsm)
otgd->fsm_running = false;

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

flush_workqueue(otgd->wq);
fsm->otg->state = OTG_STATE_UNDEFINED;
diff --git a/include/linux/usb/otg-fsm.h b/include/linux/usb/otg-fsm.h
index 22d8baa..ae9c30a 100644
--- a/include/linux/usb/otg-fsm.h
+++ b/include/linux/usb/otg-fsm.h
@@ -48,6 +48,11 @@ enum otg_fsm_timer {
/**
* struct otg_fsm - OTG state machine according to the OTG spec
*
+ * DRD mode hardware Inputs
+ *
+ * @id: TRUE for B-device, FALSE for A-device.
+ * @vbus: VBUS voltage in regulation.
+ *
* OTG hardware Inputs
*
* Common inputs for A and B device
@@ -122,7 +127,8 @@ enum otg_fsm_timer {
*/
struct otg_fsm {
/* Input */
- int id;
+ int id; /* DRD + OTG */
+ int vbus; /* DRD only */
int adp_change;
int power_up;
int a_srp_det;
diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h
index ce6f8d8..1086a0b 100644
--- a/include/linux/usb/otg.h
+++ b/include/linux/usb/otg.h
@@ -58,7 +58,7 @@ enum usb_dr_mode {

#if IS_ENABLED(CONFIG_USB_OTG)
struct otg_fsm *usb_otg_register(struct device *parent_dev,
- struct otg_fsm_ops *fsm_ops);
+ struct otg_fsm_ops *fsm_ops, bool drd_only);
int usb_otg_unregister(struct device *parent_dev);
int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
unsigned long irqflags, struct otg_hcd_ops *ops);
@@ -73,7 +73,8 @@ struct device *usb_otg_fsm_to_dev(struct otg_fsm *fsm);
#else /* CONFIG_USB_OTG */

static inline struct otg_fsm *usb_otg_register(struct device *parent_dev,
- struct otg_fsm_ops *fsm_ops)
+ struct otg_fsm_ops *fsm_ops,
+ bool drd_only)
{
return ERR_PTR(-ENOSYS);
}
--
2.1.4

2015-07-08 10:20:36

by Roger Quadros

[permalink] [raw]
Subject: [PATCH v3 11/11] usb: otg: 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 43cb2f2..c358419 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);
}
}
}
@@ -4234,8 +4237,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-07-08 10:23:31

by Roger Quadros

[permalink] [raw]
Subject: [PATCH v3 12/12] 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 6d6200e..c04305e 100644
--- a/drivers/usb/chipidea/ci.h
+++ b/drivers/usb/chipidea/ci.h
@@ -405,7 +405,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
return ci->is_otg && ci->roles[CI_ROLE_HOST] &&
ci->roles[CI_ROLE_GADGET];
#else
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 869c0cfcad..7e3459f 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-07-10 09:34:15

by Li Jun

[permalink] [raw]
Subject: Re: [PATCH v3 00/11] USB: OTG/DRD Core functionality

On Wed, Jul 08, 2015 at 01:19:26PM +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. xhci side of things for OTG/DRD use are already
> in v4.2

Do you have any plan to implement OTG(HNP) on some of your platforms based on
this OTG core?

Li Jun
>
> http://thread.gmane.org/gmane.linux.kernel/1923161
>
> DWC3 controller and platform related patches are sent separately.
>
> Changelog:

2015-07-10 09:15:51

by Li Jun

[permalink] [raw]
Subject: Re: [PATCH v3 02/11] usb: otg-fsm: support multiple instances

On Wed, Jul 08, 2015 at 01:19:28PM +0300, Roger Quadros wrote:
> Move the state_changed variable into struct otg_fsm
> so that we can support multiple instances.
>
I am not sure if multiple instances may happen since OTG protocol requires
only one OTG port can be equipped on OTG device.

Li Jun

> 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..42c6376 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,7 @@ 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;
> + fsm->state_changed = 1;
> if (fsm->otg->state == new_state)
> return 0;

"fsm->state_changed = 1;" should be put here, I think.

Li Jun

> VDBG("Set state: %s\n", usb_otg_state_string(new_state));
> @@ -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 ca508c2..243274f 100644
> --- a/include/linux/usb/otg-fsm.h
> +++ b/include/linux/usb/otg-fsm.h
> @@ -194,6 +194,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-07-13 02:30:38

by Peter Chen

[permalink] [raw]
Subject: Re: [PATCH v3 01/11] usb: otg-fsm: Add documentation for struct otg_fsm

On Wed, Jul 08, 2015 at 01:19:27PM +0300, Roger Quadros wrote:
> 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]>
> ---
> include/linux/usb/otg-fsm.h | 89 +++++++++++++++++++++++++++++++++++++++++----
> 1 file changed, 82 insertions(+), 7 deletions(-)
>
> diff --git a/include/linux/usb/otg-fsm.h b/include/linux/usb/otg-fsm.h
> index f728f18..ca508c2 100644
> --- a/include/linux/usb/otg-fsm.h
> +++ b/include/linux/usb/otg-fsm.h
> @@ -59,37 +59,112 @@ 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_ops() helpers

updated by OTG FSM helpers defined in this file

Only one tiny comment, others are ok.

Acked-by: Peter Chen <[email protected]>

--

Best Regards,
Peter Chen

2015-07-13 02:48:59

by Peter Chen

[permalink] [raw]
Subject: Re: [PATCH v3 02/11] usb: otg-fsm: support multiple instances

On Fri, Jul 10, 2015 at 04:06:43PM +0800, Li Jun wrote:
> On Wed, Jul 08, 2015 at 01:19:28PM +0300, Roger Quadros wrote:
> > Move the state_changed variable into struct otg_fsm
> > so that we can support multiple instances.
> >
> I am not sure if multiple instances may happen since OTG protocol requires
> only one OTG port can be equipped on OTG device.
>

It is ok the software can support more than spec requires, the user can
only use one and the spec may change in future :)

> Li Jun
>
> > 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..42c6376 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,7 @@ 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;
> > + fsm->state_changed = 1;
> > if (fsm->otg->state == new_state)
> > return 0;
>
> "fsm->state_changed = 1;" should be put here, I think.
>

Yes, seems it is the problem at current code.

> Li Jun
>
> > VDBG("Set state: %s\n", usb_otg_state_string(new_state));
> > @@ -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 ca508c2..243274f 100644
> > --- a/include/linux/usb/otg-fsm.h
> > +++ b/include/linux/usb/otg-fsm.h
> > @@ -194,6 +194,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
> >

--

Best Regards,
Peter Chen

2015-07-13 03:16:53

by Peter Chen

[permalink] [raw]
Subject: Re: [PATCH v3 03/11] usb: otg-fsm: Prevent build warning "VDBG" redefined

On Wed, Jul 08, 2015 at 01:19:29PM +0300, Roger Quadros wrote:
> 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]>
> ---
> 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 19d655a..6e67f94 100644
> --- a/drivers/usb/chipidea/otg_fsm.c
> +++ b/drivers/usb/chipidea/otg_fsm.c
> @@ -777,6 +777,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 42c6376..1873eb3 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);
> @@ -124,7 +125,7 @@ static int otg_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state)
> fsm->state_changed = 1;
> 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 243274f..c631dde 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)
> @@ -195,6 +181,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
>

Acked-by: Peter Chen <[email protected]>

--

Best Regards,
Peter Chen

2015-07-13 03:21:27

by Peter Chen

[permalink] [raw]
Subject: Re: [PATCH v3 04/11] otg-fsm: move usb_bus_start_enum into otg-fsm->ops

On Wed, Jul 08, 2015 at 01:19:30PM +0300, Roger Quadros wrote:
> This is to prevent missing symbol build error if OTG is
> enabled (built-in) and HCD core (CONFIG_USB) is module.
>

We may let the OTG-DRD/OTG-FSM depends on CONFIG_USB to fix it.

Peter

> Signed-off-by: Roger Quadros <[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 1873eb3..156fd25 100644
> --- a/drivers/usb/common/usb-otg-fsm.c
> +++ b/drivers/usb/common/usb-otg-fsm.c
> @@ -166,8 +166,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 c631dde..22d8baa 100644
> --- a/include/linux/usb/otg-fsm.h
> +++ b/include/linux/usb/otg-fsm.h
> @@ -198,6 +198,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
>

--

Best Regards,
Peter Chen

2015-07-13 03:25:36

by Peter Chen

[permalink] [raw]
Subject: Re: [PATCH v3 05/11] usb: hcd.h: Add OTG to HCD interface

On Wed, Jul 08, 2015 at 01:19:31PM +0300, Roger Quadros wrote:
> 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.
>

Like patch 4, Would you let the OTG depends on HCD?
OTG needs dual-role, it is reasonable.

Peter

> Signed-off-by: Roger Quadros <[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 c9aa779..4108288 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
>

--

Best Regards,
Peter Chen

2015-07-13 03:24:11

by Peter Chen

[permalink] [raw]
Subject: Re: [PATCH v3 07/11] usb: otg: add OTG core

On Wed, Jul 08, 2015 at 01:19:33PM +0300, Roger Quadros wrote:
> 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 | 768 +++++++++++++++++++++++++++++++++++++++++++
> drivers/usb/common/usb-otg.h | 71 ++++
> drivers/usb/core/Kconfig | 11 +-
> include/linux/usb/otg.h | 91 ++++-
> 8 files changed, 930 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 8133cef..b21278e 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -10640,12 +10640,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
>

USB_OTG can depends on USB || UB_GADGET?

> 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/
>

The comment like above.

> 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..0379034
> --- /dev/null
> +++ b/drivers/usb/common/usb-otg.c
> @@ -0,0 +1,768 @@
> +/**
> + * 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/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"
> +
> +/* to link timer with callback data */
> +struct otg_timer {
> + struct hrtimer timer;
> + ktime_t timeout;
> + /* callback data */
> + int *timeout_bit;
> + struct otg_data *otgd;
> +};
> +
> +struct otg_hcd {
> + struct usb_hcd *hcd;
> + unsigned int irqnum;
> + unsigned long irqflags;
> + struct otg_hcd_ops *ops;
> +};
> +
> +struct otg_data {
> + struct device *dev; /* HCD & GCD's parent device */
> +
> + struct otg_fsm fsm;
> + /* HCD, GCD and usb_otg_state are present in otg_fsm->otg
> + * HCD is bus_to_hcd(fsm->otg->host)
> + * GCD is fsm->otg->gadget
> + */
> + struct otg_fsm_ops fsm_ops; /* private copy for override */
> + struct usb_otg otg; /* allocator for fsm->otg */
> +
> + struct otg_hcd primary_hcd;
> + struct otg_hcd shared_hcd;
> +
> + struct otg_gadget_ops *gadget_ops; /* interface to gadget f/w */
> +
> + /* saved hooks to OTG device */
> + int (*start_host)(struct otg_fsm *fsm, int on);
> + int (*start_gadget)(struct otg_fsm *fsm, int on);
> +
> + struct list_head list;
> +
> + struct work_struct work; /* OTG FSM work */
> + struct workqueue_struct *wq;
> +
> + struct otg_timer timers[NUM_OTG_FSM_TIMERS];
> +
> + bool fsm_running;
> + /* use otg->fsm.lock for serializing access */
> +};
> +

Would you consider enhance struct usb_otg, and instead of this
structure, I am wonder similar structures may increase the confuse
in future.

Peter

> +/* OTG device list */
> +LIST_HEAD(otg_list);
> +static DEFINE_MUTEX(otg_list_mutex);
> +
> +static int usb_otg_hcd_is_primary_hcd(struct usb_hcd *hcd)
> +{
> + if (!hcd->primary_hcd)
> + return 1;
> + return hcd == hcd->primary_hcd;
> +}
> +
> +/**
> + * check if device is in our OTG list and return
> + * otg_data, else NULL.
> + *
> + * otg_list_mutex must be held.
> + */
> +static struct otg_data *usb_otg_device_get_otgd(struct device *parent_dev)
> +{
> + struct otg_data *otgd;
> +
> + list_for_each_entry(otgd, &otg_list, list) {
> + if (otgd->dev == parent_dev)
> + return otgd;
> + }
> +
> + return NULL;
> +}
> +
> +/**
> + * 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 otg_data *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 otg_data *otgd)
> +{
> + struct otg_fsm *fsm = &otgd->fsm;
> +
> + 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);
> +
> + otg_timer_init(A_WAIT_ENUM, otgd, set_tmout, TB_SRP_FAIL, NULL);
> +}
> +
> +/**
> + * OTG FSM ops function to add timer
> + */
> +static void usb_otg_add_timer(struct otg_fsm *fsm, enum otg_fsm_timer id)
> +{
> + struct otg_data *otgd = container_of(fsm, struct otg_data, 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 otg_data *otgd = container_of(fsm, struct otg_data, fsm);
> + struct hrtimer *timer = &otgd->timers[id].timer;
> +
> + hrtimer_cancel(timer);
> +}
> +
> +/**
> + * OTG FSM ops function to start/stop host
> + */
> +static int usb_otg_start_host(struct otg_fsm *fsm, int on)
> +{
> + struct otg_data *otgd = container_of(fsm, struct otg_data, 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) {
> + /* OTG device operations */
> + if (otgd->start_host)
> + otgd->start_host(fsm, 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);
> +
> + /* OTG device operations */
> + if (otgd->start_host)
> + otgd->start_host(fsm, on);
> + }
> +
> + return 0;
> +}
> +
> +/**
> + * OTG FSM ops function to start/stop gadget
> + */
> +static int usb_otg_start_gadget(struct otg_fsm *fsm, int on)
> +{
> + struct otg_data *otgd = container_of(fsm, struct otg_data, 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) {
> + /* OTG device operations */
> + if (otgd->start_gadget)
> + otgd->start_gadget(fsm, on);
> +
> + otgd->gadget_ops->start(fsm->otg->gadget);
> + } else {
> + otgd->gadget_ops->stop(fsm->otg->gadget);
> +
> + /* OTG device operations */
> + if (otgd->start_gadget)
> + otgd->start_gadget(fsm, on);
> + }
> +
> + return 0;
> +}
> +
> +/**
> + * OTG FSM work function
> + */
> +static void usb_otg_work(struct work_struct *work)
> +{
> + struct otg_data *otgd = container_of(work, struct otg_data, work);
> +
> + otg_statemachine(&otgd->fsm);
> +}
> +
> +/**
> + * usb_otg_register() - Register the OTG device to OTG core
> + * @parent_device: parent device of Host & Gadget controllers.
> + * @otg_fsm_ops: otg state machine ops.
> + *
> + * Register parent device that contains both HCD and GCD into
> + * the USB OTG core. HCD and GCD will be prevented from starting
> + * till both are available for use.
> + *
> + * Return: struct otg_fsm * if success, NULL if error.
> + */
> +struct otg_fsm *usb_otg_register(struct device *parent_dev,
> + struct otg_fsm_ops *fsm_ops)
> +{
> + struct otg_data *otgd;
> + int ret = 0;
> +
> + if (!parent_dev || !fsm_ops)
> + return ERR_PTR(-EINVAL);
> +
> + /* already in list? */
> + mutex_lock(&otg_list_mutex);
> + if (usb_otg_device_get_otgd(parent_dev)) {
> + dev_err(parent_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 = parent_dev;
> + INIT_WORK(&otgd->work, usb_otg_work);
> + otgd->wq = create_singlethread_workqueue("usb_otg");
> + if (!otgd->wq) {
> + dev_err(parent_dev, "otg: %s: can't create workqueue\n",
> + __func__);
> + ret = -ENODEV;
> + goto err_wq;
> + }
> +
> + usb_otg_init_timers(otgd);
> +
> + /* save original start host/gadget ops */
> + otgd->start_host = fsm_ops->start_host;
> + otgd->start_gadget = fsm_ops->start_gadget;
> + /* create copy of original ops */
> + otgd->fsm_ops = *fsm_ops;
> + /* override ops */
> + otgd->fsm_ops.start_host = usb_otg_start_host;
> + otgd->fsm_ops.start_gadget = usb_otg_start_gadget;
> + /* FIXME: we ignore caller's timer ops */
> + otgd->fsm_ops.add_timer = usb_otg_add_timer;
> + otgd->fsm_ops.del_timer = usb_otg_del_timer;
> + /* set otg ops */
> + otgd->fsm.ops = &otgd->fsm_ops;
> + otgd->fsm.otg = &otgd->otg;
> +
> + mutex_init(&otgd->fsm.lock);
> +
> + list_add_tail(&otgd->list, &otg_list);
> + mutex_unlock(&otg_list_mutex);
> + 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
> + * @parent_device: parent device of Host & Gadget controllers.
> + *
> + * Unregister parent OTG deviced from USB OTG core.
> + * Prevents unregistering till both Host and Gadget controllers
> + * have unregistered from the OTG core.
> + *
> + * Return: 0 on success, error value otherwise.
> + */
> +int usb_otg_unregister(struct device *parent_dev)
> +{
> + struct otg_data *otgd;
> +
> + mutex_lock(&otg_list_mutex);
> + otgd = usb_otg_device_get_otgd(parent_dev);
> + if (!otgd) {
> + dev_err(parent_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(parent_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 otg_data *otgd = container_of(fsm, struct otg_data, 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 otg_data *otgd = container_of(fsm, struct otg_data, 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 otg_data *otgd = container_of(fsm, struct otg_data, 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 otg_data *otgd;
> +
> + mutex_lock(&otg_list_mutex);
> + otgd = usb_otg_device_get_otgd(hcd_gcd_device->parent);
> + if (!otgd) {
> + dev_err(hcd_gcd_device, "otg: %s: invalid host/gadget device\n",
> + __func__);
> + mutex_unlock(&otg_list_mutex);
> + return -ENODEV;
> + }
> +
> + mutex_unlock(&otg_list_mutex);
> + 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 otg_data *otgd;
> + struct device *otg_dev = hcd->self.controller->parent;
> +
> + mutex_lock(&otg_list_mutex);
> + otgd = usb_otg_device_get_otgd(otg_dev);
> + if (!otgd) {
> + dev_dbg(otg_dev, "otg: %s: device not registered to otg core\n",
> + __func__);
> + mutex_unlock(&otg_list_mutex);
> + return -EINVAL;
> + }
> +
> + mutex_unlock(&otg_list_mutex);
> + /* 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 otg_data *otgd;
> + struct usb_bus *bus = hcd_to_bus(hcd);
> + struct device *otg_dev = bus->controller->parent;
> +
> + mutex_lock(&otg_list_mutex);
> + otgd = usb_otg_device_get_otgd(otg_dev);
> + if (!otgd) {
> + dev_err(otg_dev, "otg: %s: device not registered to otg core\n",
> + __func__);
> + mutex_unlock(&otg_list_mutex);
> + return -EINVAL;
> + }
> +
> + mutex_unlock(&otg_list_mutex);
> +
> + 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(bus->controller));
> + } else if (hcd == otgd->shared_hcd.hcd) {
> + otgd->shared_hcd.hcd = NULL;
> + dev_info(otg_dev, "otg: shared host %s unregistered\n",
> + dev_name(bus->controller));
> + } else {
> + dev_err(otg_dev, "otg: host %s wasn't registered with otg\n",
> + dev_name(bus->controller));
> + 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 otg_data *otgd;
> + struct device *otg_dev = gadget->dev.parent;
> +
> + mutex_lock(&otg_list_mutex);
> + otgd = usb_otg_device_get_otgd(otg_dev);
> + if (!otgd) {
> + dev_err(otg_dev, "otg: %s: device not registered to otg core\n",
> + __func__);
> + mutex_unlock(&otg_list_mutex);
> + return -EINVAL;
> + }
> +
> + mutex_unlock(&otg_list_mutex);
> +
> + 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 otg_data *otgd;
> + struct device *otg_dev = gadget->dev.parent;
> +
> + mutex_lock(&otg_list_mutex);
> + otgd = usb_otg_device_get_otgd(otg_dev);
> + if (!otgd) {
> + dev_err(otg_dev, "otg: %s: device not registered to otg core\n",
> + __func__);
> + mutex_unlock(&otg_list_mutex);
> + return -EINVAL;
> + }
> +
> + mutex_unlock(&otg_list_mutex);
> +
> + 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 otg_data *otgd = container_of(fsm, struct otg_data, 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 52661c5..ce6f8d8 100644
> --- a/include/linux/usb/otg.h
> +++ b/include/linux/usb/otg.h
> @@ -10,6 +10,11 @@
> #define __LINUX_USB_OTG_H
>
> #include <linux/phy/phy.h>
> +#include <linux/device.h>
> +#include <linux/usb.h>
> +#include <linux/usb/hcd.h>
> +#include <linux/usb/gadget.h>
> +#include <linux/usb/otg-fsm.h>
> #include <linux/usb/phy.h>
>
> struct usb_otg {
> @@ -23,6 +28,7 @@ struct usb_otg {
>
> enum usb_otg_state state;
>
> +/*------------- deprecated interface -----------------------------*/
> /* bind/unbind the host controller */
> int (*set_host)(struct usb_otg *otg, struct usb_bus *host);
>
> @@ -38,11 +44,85 @@ struct usb_otg {
>
> /* start or continue HNP role switch */
> int (*start_hnp)(struct usb_otg *otg);
> -
> +/*---------------------------------------------------------------*/
> };
>
> 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 *parent_dev,
> + struct otg_fsm_ops *fsm_ops);
> +int usb_otg_unregister(struct device *parent_dev);
> +int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
> + unsigned long irqflags, struct otg_hcd_ops *ops);
> +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);
> +
> +#else /* CONFIG_USB_OTG */
> +
> +static inline struct otg_fsm *usb_otg_register(struct device *parent_dev,
> + struct otg_fsm_ops *fsm_ops)
> +{
> + return ERR_PTR(-ENOSYS);
> +}
> +
> +static inline int usb_otg_unregister(struct device *parent_dev)
> +{
> + return -ENOSYS;
> +}
> +
> +static inline int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
> + unsigned long irqflags,
> + struct otg_hcd_ops *ops)
> +{
> + return -ENOSYS;
> +}
> +
> +static inline int usb_otg_unregister_hcd(struct usb_hcd *hcd)
> +{
> + return -ENOSYS;
> +}
> +
> +static inline int usb_otg_register_gadget(struct usb_gadget *gadget,
> + struct otg_gadget_ops *ops)
> +{
> + return -ENOSYS;
> +}
> +
> +static inline int usb_otg_unregister_gadget(struct usb_gadget *gadget)
> +{
> + return -ENOSYS;
> +}
> +
> +static inline void usb_otg_sync_inputs(struct otg_fsm *fsm)
> +{
> +}
> +
> +static inline int usb_otg_kick_fsm(struct device *hcd_gcd_device)
> +{
> + return -ENOSYS;
> +}
> +
> +static inline struct device *usb_otg_fsm_to_dev(struct otg_fsm *fsm)
> +{
> + return NULL;
> +}
> +
> +#endif /* CONFIG_USB_OTG */
> +
> +/*------------- deprecated interface -----------------------------*/
> /* Context: can sleep */
> static inline int
> otg_start_hnp(struct usb_otg *otg)
> @@ -94,14 +174,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
>

--

Best Regards,
Peter Chen

2015-07-13 10:06:22

by Roger Quadros

[permalink] [raw]
Subject: Re: [PATCH v3 01/11] usb: otg-fsm: Add documentation for struct otg_fsm

On 13/07/15 04:21, Peter Chen wrote:
> On Wed, Jul 08, 2015 at 01:19:27PM +0300, Roger Quadros wrote:
>> 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]>
>> ---
>> include/linux/usb/otg-fsm.h | 89 +++++++++++++++++++++++++++++++++++++++++----
>> 1 file changed, 82 insertions(+), 7 deletions(-)
>>
>> diff --git a/include/linux/usb/otg-fsm.h b/include/linux/usb/otg-fsm.h
>> index f728f18..ca508c2 100644
>> --- a/include/linux/usb/otg-fsm.h
>> +++ b/include/linux/usb/otg-fsm.h
>> @@ -59,37 +59,112 @@ 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_ops() helpers
>
> updated by OTG FSM helpers defined in this file

OK. Will fix it.

>
> Only one tiny comment, others are ok.
>
> Acked-by: Peter Chen <[email protected]>
>

cheers,
-roger

2015-07-13 10:14:10

by Roger Quadros

[permalink] [raw]
Subject: Re: [PATCH v3 04/11] otg-fsm: move usb_bus_start_enum into otg-fsm->ops

Peter,

On 13/07/15 04:58, Peter Chen wrote:
> On Wed, Jul 08, 2015 at 01:19:30PM +0300, Roger Quadros wrote:
>> This is to prevent missing symbol build error if OTG is
>> enabled (built-in) and HCD core (CONFIG_USB) is module.
>>
>
> We may let the OTG-DRD/OTG-FSM depends on CONFIG_USB to fix it.

CONFIG_OTG already depends on CONFIG_USB as it is a sub-option of
CONFIG_USB. It doesn't depend on CONFIG_USB_GADGET and that can
be fixed.

But dependency is not the problem here. Symbols not available to
OTG driver when USB/GADGET is 'm' is the problem.

e.g.
CONFIG_USB_OTG is always built-in.
we need to work if CONFIG_USB is 'm'/'y'
_and_ if CONFIG_USB_GADGET is 'm'/'y'

cheers,
-roger

>
> Peter
>
>> Signed-off-by: Roger Quadros <[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 1873eb3..156fd25 100644
>> --- a/drivers/usb/common/usb-otg-fsm.c
>> +++ b/drivers/usb/common/usb-otg-fsm.c
>> @@ -166,8 +166,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 c631dde..22d8baa 100644
>> --- a/include/linux/usb/otg-fsm.h
>> +++ b/include/linux/usb/otg-fsm.h
>> @@ -198,6 +198,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-07-13 10:21:03

by Roger Quadros

[permalink] [raw]
Subject: Re: [PATCH v3 07/11] usb: otg: add OTG core

On 13/07/15 05:14, Peter Chen wrote:
> On Wed, Jul 08, 2015 at 01:19:33PM +0300, Roger Quadros wrote:
>> 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 | 768 +++++++++++++++++++++++++++++++++++++++++++
>> drivers/usb/common/usb-otg.h | 71 ++++
>> drivers/usb/core/Kconfig | 11 +-
>> include/linux/usb/otg.h | 91 ++++-
>> 8 files changed, 930 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 8133cef..b21278e 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -10640,12 +10640,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
>>
>
> USB_OTG can depends on USB || UB_GADGET?

I didn't understand. The above is for USB_COMMON.

>
>> 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/
>>
>
> The comment like above.

What should it look like?

>
>> 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..0379034
>> --- /dev/null
>> +++ b/drivers/usb/common/usb-otg.c
>> @@ -0,0 +1,768 @@
>> +/**
>> + * 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/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"
>> +
>> +/* to link timer with callback data */
>> +struct otg_timer {
>> + struct hrtimer timer;
>> + ktime_t timeout;
>> + /* callback data */
>> + int *timeout_bit;
>> + struct otg_data *otgd;
>> +};
>> +
>> +struct otg_hcd {
>> + struct usb_hcd *hcd;
>> + unsigned int irqnum;
>> + unsigned long irqflags;
>> + struct otg_hcd_ops *ops;
>> +};
>> +
>> +struct otg_data {
>> + struct device *dev; /* HCD & GCD's parent device */
>> +
>> + struct otg_fsm fsm;
>> + /* HCD, GCD and usb_otg_state are present in otg_fsm->otg
>> + * HCD is bus_to_hcd(fsm->otg->host)
>> + * GCD is fsm->otg->gadget
>> + */
>> + struct otg_fsm_ops fsm_ops; /* private copy for override */
>> + struct usb_otg otg; /* allocator for fsm->otg */
>> +
>> + struct otg_hcd primary_hcd;
>> + struct otg_hcd shared_hcd;
>> +
>> + struct otg_gadget_ops *gadget_ops; /* interface to gadget f/w */
>> +
>> + /* saved hooks to OTG device */
>> + int (*start_host)(struct otg_fsm *fsm, int on);
>> + int (*start_gadget)(struct otg_fsm *fsm, int on);
>> +
>> + struct list_head list;
>> +
>> + struct work_struct work; /* OTG FSM work */
>> + struct workqueue_struct *wq;
>> +
>> + struct otg_timer timers[NUM_OTG_FSM_TIMERS];
>> +
>> + bool fsm_running;
>> + /* use otg->fsm.lock for serializing access */
>> +};
>> +
>
> Would you consider enhance struct usb_otg, and instead of this
> structure, I am wonder similar structures may increase the confuse
> in future.

Yes, that should be possible.

cheers,
-roger

2015-07-13 10:24:19

by Roger Quadros

[permalink] [raw]
Subject: Re: [PATCH v3 00/11] USB: OTG/DRD Core functionality

Hi,

On 10/07/15 10:58, Li Jun wrote:
> On Wed, Jul 08, 2015 at 01:19:26PM +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. xhci side of things for OTG/DRD use are already
>> in v4.2
>
> Do you have any plan to implement OTG(HNP) on some of your platforms based on
> this OTG core?

Not yet but Joao Pinto was interested to implement full OTG for dwc3
controllers.

Would you be interested to try out one of your platforms so that we can
address any issues early?

cheers,
-roger

>
> Li Jun
>>
>> http://thread.gmane.org/gmane.linux.kernel/1923161
>>
>> DWC3 controller and platform related patches are sent separately.
>>
>> Changelog:

2015-07-13 14:28:10

by Li Jun

[permalink] [raw]
Subject: Re: [PATCH v3 00/11] USB: OTG/DRD Core functionality

On Mon, Jul 13, 2015 at 01:24:05PM +0300, Roger Quadros wrote:
> Hi,
>
> On 10/07/15 10:58, Li Jun wrote:
> > On Wed, Jul 08, 2015 at 01:19:26PM +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. xhci side of things for OTG/DRD use are already
> >> in v4.2
> >
> > Do you have any plan to implement OTG(HNP) on some of your platforms based on
> > this OTG core?
>
> Not yet but Joao Pinto was interested to implement full OTG for dwc3
> controllers.
>
> Would you be interested to try out one of your platforms so that we can
> address any issues early?
>
> cheers,
> -roger
>

Yes, I am considering to try out OTG on chipidea driver for FSL i.mx6x platform
, which will based on your OTG core and my previous gadget update patchset.

I will comment your patchset if find any issues. Since there is still much work
to do to complete OTG HNP/SRP, so I assume this will not be done in a short time.

Li Jun
> >
> > Li Jun
> >>
> >> http://thread.gmane.org/gmane.linux.kernel/1923161
> >>
> >> DWC3 controller and platform related patches are sent separately.
> >>
> >> Changelog:

2015-07-13 19:14:47

by Andrew Bresticker

[permalink] [raw]
Subject: Re: [PATCH v3 00/11] USB: OTG/DRD Core functionality

Hi Roger,

On Wed, Jul 8, 2015 at 3:19 AM, Roger Quadros <[email protected]> wrote:
> Usage model:
> -----------
>
> - The OTG controller device is assumed to be the parent of
> the host and gadget controller. It must call usb_otg_register()
> before populating the host and gadget devices so that the OTG
> core is aware that it is an OTG device before the host & gadget
> register. The OTG controller must provide struct otg_fsm_ops *
> which will be called by the OTG core depending on OTG bus state.

I'm wondering if the requirement that the OTG controller be the parent
of the USB host/device-controllers makes sense. For some context, I'm
working on adding dual-role support for Tegra210, specifically on a
system with USB Type-C. On Tegra, the USB host-controller and USB
device-controller are two separate IP blocks (XUSB host and XUSB
device) with another, separate, IP block (XUSB padctl) for the USB PHY
and OTG support. In the non-Type-C case, your OTG framework could
work well, though it's debatable as to whether or not the XUSB padctl
device should be a parent to the XUSB host/device-controller devices
(currently it isn't - it's just a PHY provider). But in the Type-C
case, it's an off-chip embedded controller that determines the
dual-role status of the Type-C port, so the above requirement doesn't
make sense at all.

My idea was to have the OTG/DRD controller explicitly specify its host
and device controllers, so in DT, something like:

otg-controller {
...
device-controller = <&usb_device>;
host-controller = <&usb_host>;
...
};

usb_device: usb-device@.... {
...
};

usb_host: usb-host@... {
...
};

What do you think?

Thanks,
Andrew

2015-07-14 01:43:43

by Peter Chen

[permalink] [raw]
Subject: Re: [PATCH v3 04/11] otg-fsm: move usb_bus_start_enum into otg-fsm->ops

On Mon, Jul 13, 2015 at 01:13:54PM +0300, Roger Quadros wrote:
> Peter,
>
> On 13/07/15 04:58, Peter Chen wrote:
> > On Wed, Jul 08, 2015 at 01:19:30PM +0300, Roger Quadros wrote:
> >> This is to prevent missing symbol build error if OTG is
> >> enabled (built-in) and HCD core (CONFIG_USB) is module.
> >>
> >
> > We may let the OTG-DRD/OTG-FSM depends on CONFIG_USB to fix it.
>
> CONFIG_OTG already depends on CONFIG_USB as it is a sub-option of
> CONFIG_USB. It doesn't depend on CONFIG_USB_GADGET and that can
> be fixed.
>
> But dependency is not the problem here. Symbols not available to
> OTG driver when USB/GADGET is 'm' is the problem.
>
> e.g.
> CONFIG_USB_OTG is always built-in.
> we need to work if CONFIG_USB is 'm'/'y'
> _and_ if CONFIG_USB_GADGET is 'm'/'y'
>

below should fix this issue, but we may need to make some
changes for code which are defined by CONFIG_USB_OTG.

diff --git a/drivers/usb/core/Kconfig b/drivers/usb/core/Kconfig
index a99c89e..5e374ad 100644
--- a/drivers/usb/core/Kconfig
+++ b/drivers/usb/core/Kconfig
@@ -42,8 +42,9 @@ config USB_DYNAMIC_MINORS
If you are unsure about this, say N here.

config USB_OTG
- bool "OTG support"
+ tristate "OTG support"
depends on PM
+ depends on USB && USB_GADGET
default n
help
The most notable feature of
USB OTG is support for a
>
> >
> > Peter
> >
> >> Signed-off-by: Roger Quadros <[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 1873eb3..156fd25 100644
> >> --- a/drivers/usb/common/usb-otg-fsm.c
> >> +++ b/drivers/usb/common/usb-otg-fsm.c
> >> @@ -166,8 +166,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 c631dde..22d8baa 100644
> >> --- a/include/linux/usb/otg-fsm.h
> >> +++ b/include/linux/usb/otg-fsm.h
> >> @@ -198,6 +198,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
> >>
> >

--

Best Regards,
Peter Chen

2015-07-14 02:08:56

by Peter Chen

[permalink] [raw]
Subject: Re: [PATCH v3 00/11] USB: OTG/DRD Core functionality

On Mon, Jul 13, 2015 at 12:14:43PM -0700, Andrew Bresticker wrote:
> Hi Roger,
>
> On Wed, Jul 8, 2015 at 3:19 AM, Roger Quadros <[email protected]> wrote:
> > Usage model:
> > -----------
> >
> > - The OTG controller device is assumed to be the parent of
> > the host and gadget controller. It must call usb_otg_register()
> > before populating the host and gadget devices so that the OTG
> > core is aware that it is an OTG device before the host & gadget
> > register. The OTG controller must provide struct otg_fsm_ops *
> > which will be called by the OTG core depending on OTG bus state.
>
> I'm wondering if the requirement that the OTG controller be the parent
> of the USB host/device-controllers makes sense. For some context, I'm
> working on adding dual-role support for Tegra210, specifically on a
> system with USB Type-C. On Tegra, the USB host-controller and USB
> device-controller are two separate IP blocks (XUSB host and XUSB
> device) with another, separate, IP block (XUSB padctl) for the USB PHY
> and OTG support. In the non-Type-C case, your OTG framework could
> work well, though it's debatable as to whether or not the XUSB padctl
> device should be a parent to the XUSB host/device-controller devices
> (currently it isn't - it's just a PHY provider). But in the Type-C
> case, it's an off-chip embedded controller that determines the
> dual-role status of the Type-C port, so the above requirement doesn't
> make sense at all.

Hi Andrew,

I think your problem is how to add your core driver to manage device and
host functionality together, and once you find how (through padctl/type-c
controller) to do it based on current code, it will be clear how to use roger
proposal framework at that time.

Most of current core drivers, we use extcon driver (through gpio) or USB
vbus/id pin (through internal registers) to manager roles.

>
> My idea was to have the OTG/DRD controller explicitly specify its host
> and device controllers, so in DT, something like:
>
> otg-controller {
> ...
> device-controller = <&usb_device>;
> host-controller = <&usb_host>;
> ...
> };
>
> usb_device: usb-device@.... {
> ...
> };
>
> usb_host: usb-host@... {
> ...
> };
>
> What do you think?
>
> Thanks,
> Andrew

--

Best Regards,
Peter Chen

2015-07-14 18:18:34

by Andrew Bresticker

[permalink] [raw]
Subject: Re: [PATCH v3 00/11] USB: OTG/DRD Core functionality

Hi Peter,

On Mon, Jul 13, 2015 at 5:59 PM, Peter Chen <[email protected]> wrote:
> On Mon, Jul 13, 2015 at 12:14:43PM -0700, Andrew Bresticker wrote:
>> Hi Roger,
>>
>> On Wed, Jul 8, 2015 at 3:19 AM, Roger Quadros <[email protected]> wrote:
>> > Usage model:
>> > -----------
>> >
>> > - The OTG controller device is assumed to be the parent of
>> > the host and gadget controller. It must call usb_otg_register()
>> > before populating the host and gadget devices so that the OTG
>> > core is aware that it is an OTG device before the host & gadget
>> > register. The OTG controller must provide struct otg_fsm_ops *
>> > which will be called by the OTG core depending on OTG bus state.
>>
>> I'm wondering if the requirement that the OTG controller be the parent
>> of the USB host/device-controllers makes sense. For some context, I'm
>> working on adding dual-role support for Tegra210, specifically on a
>> system with USB Type-C. On Tegra, the USB host-controller and USB
>> device-controller are two separate IP blocks (XUSB host and XUSB
>> device) with another, separate, IP block (XUSB padctl) for the USB PHY
>> and OTG support. In the non-Type-C case, your OTG framework could
>> work well, though it's debatable as to whether or not the XUSB padctl
>> device should be a parent to the XUSB host/device-controller devices
>> (currently it isn't - it's just a PHY provider). But in the Type-C
>> case, it's an off-chip embedded controller that determines the
>> dual-role status of the Type-C port, so the above requirement doesn't
>> make sense at all.
>
> Hi Andrew,
>
> I think your problem is how to add your core driver to manage device and
> host functionality together, and once you find how (through padctl/type-c
> controller) to do it based on current code, it will be clear how to use roger
> proposal framework at that time.
>
> Most of current core drivers, we use extcon driver (through gpio) or USB
> vbus/id pin (through internal registers) to manager roles.

Right, currently I'm modeling the Type-C controller as an extcon
device and handle the role-changes in the core drivers, but that
doesn't really make sense for the non-Type-C case where we use the
XUSB padctl controller and need a full OTG state-machine. Roger's new
OTG/DRD framework would fit my situation perfectly since it makes the
host/device-controller drivers independent from all the
OTG/role-changing logic. The only issue is the requirement that the
OTG/DRD controller be the parent device of the host/device
controllers.

Thanks,
Andrew

2015-07-15 03:22:58

by Peter Chen

[permalink] [raw]
Subject: Re: [PATCH v3 00/11] USB: OTG/DRD Core functionality

On Tue, Jul 14, 2015 at 11:18:30AM -0700, Andrew Bresticker wrote:
> Hi Peter,
>
> On Mon, Jul 13, 2015 at 5:59 PM, Peter Chen <[email protected]> wrote:
> > On Mon, Jul 13, 2015 at 12:14:43PM -0700, Andrew Bresticker wrote:
> >> Hi Roger,
> >>
> >> On Wed, Jul 8, 2015 at 3:19 AM, Roger Quadros <[email protected]> wrote:
> >> > Usage model:
> >> > -----------
> >> >
> >> > - The OTG controller device is assumed to be the parent of
> >> > the host and gadget controller. It must call usb_otg_register()
> >> > before populating the host and gadget devices so that the OTG
> >> > core is aware that it is an OTG device before the host & gadget
> >> > register. The OTG controller must provide struct otg_fsm_ops *
> >> > which will be called by the OTG core depending on OTG bus state.
> >>
> >> I'm wondering if the requirement that the OTG controller be the parent
> >> of the USB host/device-controllers makes sense. For some context, I'm
> >> working on adding dual-role support for Tegra210, specifically on a
> >> system with USB Type-C. On Tegra, the USB host-controller and USB
> >> device-controller are two separate IP blocks (XUSB host and XUSB
> >> device) with another, separate, IP block (XUSB padctl) for the USB PHY
> >> and OTG support. In the non-Type-C case, your OTG framework could
> >> work well, though it's debatable as to whether or not the XUSB padctl
> >> device should be a parent to the XUSB host/device-controller devices
> >> (currently it isn't - it's just a PHY provider). But in the Type-C
> >> case, it's an off-chip embedded controller that determines the
> >> dual-role status of the Type-C port, so the above requirement doesn't
> >> make sense at all.
> >
> > Hi Andrew,
> >
> > I think your problem is how to add your core driver to manage device and
> > host functionality together, and once you find how (through padctl/type-c
> > controller) to do it based on current code, it will be clear how to use roger
> > proposal framework at that time.
> >
> > Most of current core drivers, we use extcon driver (through gpio) or USB
> > vbus/id pin (through internal registers) to manager roles.
>
> Right, currently I'm modeling the Type-C controller as an extcon
> device and handle the role-changes in the core drivers, but that
> doesn't really make sense for the non-Type-C case where we use the
> XUSB padctl controller and need a full OTG state-machine.

The full OTG FSM is only applied if your board needs it, you can
disable it through dts. Jun [1] and Roger's patchset are for it.

> Roger's new
> OTG/DRD framework would fit my situation perfectly since it makes the
> host/device-controller drivers independent from all the
> OTG/role-changing logic. The only issue is the requirement that the
> OTG/DRD controller be the parent device of the host/device
> controllers.
>

The core device is the parent for host/device device, the OTG core
just use the pointer of it, Roger does an example using dwc3 [2].

[1] http://www.spinics.net/lists/linux-usb/msg127110.html
[2] http://www.spinics.net/lists/linux-usb/msg126999.html
--

Best Regards,
Peter Chen

2015-07-15 13:07:33

by Roger Quadros

[permalink] [raw]
Subject: Re: [PATCH v3 00/11] USB: OTG/DRD Core functionality

On 13/07/15 16:04, Li Jun wrote:
> On Mon, Jul 13, 2015 at 01:24:05PM +0300, Roger Quadros wrote:
>> Hi,
>>
>> On 10/07/15 10:58, Li Jun wrote:
>>> On Wed, Jul 08, 2015 at 01:19:26PM +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. xhci side of things for OTG/DRD use are already
>>>> in v4.2
>>>
>>> Do you have any plan to implement OTG(HNP) on some of your platforms based on
>>> this OTG core?
>>
>> Not yet but Joao Pinto was interested to implement full OTG for dwc3
>> controllers.
>>
>> Would you be interested to try out one of your platforms so that we can
>> address any issues early?
>>
>> cheers,
>> -roger
>>
>
> Yes, I am considering to try out OTG on chipidea driver for FSL i.mx6x platform
> , which will based on your OTG core and my previous gadget update patchset.

Great :)
>
> I will comment your patchset if find any issues. Since there is still much work
> to do to complete OTG HNP/SRP, so I assume this will not be done in a short time.

Yes. My plan was to first get DRD working reliably and then add missing HNP/SRP stuff.
The first user of HNP/SRP will know what issues are and what needs to be added.

cheers,
-roger

2015-07-15 13:27:05

by Roger Quadros

[permalink] [raw]
Subject: Re: [PATCH v3 00/11] USB: OTG/DRD Core functionality

Hi Andrew,

On 13/07/15 22:14, Andrew Bresticker wrote:
> Hi Roger,
>
> On Wed, Jul 8, 2015 at 3:19 AM, Roger Quadros <[email protected]> wrote:
>> Usage model:
>> -----------
>>
>> - The OTG controller device is assumed to be the parent of
>> the host and gadget controller. It must call usb_otg_register()
>> before populating the host and gadget devices so that the OTG
>> core is aware that it is an OTG device before the host & gadget
>> register. The OTG controller must provide struct otg_fsm_ops *
>> which will be called by the OTG core depending on OTG bus state.
>
> I'm wondering if the requirement that the OTG controller be the parent
> of the USB host/device-controllers makes sense. For some context, I'm
> working on adding dual-role support for Tegra210, specifically on a
> system with USB Type-C. On Tegra, the USB host-controller and USB
> device-controller are two separate IP blocks (XUSB host and XUSB
> device) with another, separate, IP block (XUSB padctl) for the USB PHY
> and OTG support. In the non-Type-C case, your OTG framework could
> work well, though it's debatable as to whether or not the XUSB padctl
> device should be a parent to the XUSB host/device-controller devices
> (currently it isn't - it's just a PHY provider). But in the Type-C
> case, it's an off-chip embedded controller that determines the
> dual-role status of the Type-C port, so the above requirement doesn't
> make sense at all.
>
> My idea was to have the OTG/DRD controller explicitly specify its host
> and device controllers, so in DT, something like:
>
> otg-controller {
> ...
> device-controller = <&usb_device>;
> host-controller = <&usb_host>;
> ...
> };
>
> usb_device: usb-device@.... {
> ...
> };
>
> usb_host: usb-host@... {
> ...
> };
>
> What do you think?

I agree that we need to support your use case but how to do it
is not yet clear to me.

In your above example the otg controller knows what are the host
and gadget controllers but the host/gadget devices don't
know who is their otg controller.

So the problem is that when usb_otg_register_hcd/gadget() is called
we need to get a handle to the otg controller.

One solution I see is to iterate over the registered otg_controller_list
and check if we match the host/gadget controller in there.

Then there is also a possibility that host/gadget controllers get
registered before the OTG controller. Then we can't know for sure if
the host/gadget controller was meant for dual-role operation or not
and it will resort to single role operation.

Any idea to prevent the above situation?

Maybe we need to add some logic in host/gadget cores to check if the port
is meant for dual-role use and defer probe if OTG controller is not yet
registered?

cheers,
-roger

2015-07-15 13:30:43

by Roger Quadros

[permalink] [raw]
Subject: Re: [PATCH v3 04/11] otg-fsm: move usb_bus_start_enum into otg-fsm->ops

On 14/07/15 03:34, Peter Chen wrote:
> On Mon, Jul 13, 2015 at 01:13:54PM +0300, Roger Quadros wrote:
>> Peter,
>>
>> On 13/07/15 04:58, Peter Chen wrote:
>>> On Wed, Jul 08, 2015 at 01:19:30PM +0300, Roger Quadros wrote:
>>>> This is to prevent missing symbol build error if OTG is
>>>> enabled (built-in) and HCD core (CONFIG_USB) is module.
>>>>
>>>
>>> We may let the OTG-DRD/OTG-FSM depends on CONFIG_USB to fix it.
>>
>> CONFIG_OTG already depends on CONFIG_USB as it is a sub-option of
>> CONFIG_USB. It doesn't depend on CONFIG_USB_GADGET and that can
>> be fixed.
>>
>> But dependency is not the problem here. Symbols not available to
>> OTG driver when USB/GADGET is 'm' is the problem.
>>
>> e.g.
>> CONFIG_USB_OTG is always built-in.
>> we need to work if CONFIG_USB is 'm'/'y'
>> _and_ if CONFIG_USB_GADGET is 'm'/'y'
>>
>
> below should fix this issue, but we may need to make some
> changes for code which are defined by CONFIG_USB_OTG.
>
> diff --git a/drivers/usb/core/Kconfig b/drivers/usb/core/Kconfig
> index a99c89e..5e374ad 100644
> --- a/drivers/usb/core/Kconfig
> +++ b/drivers/usb/core/Kconfig
> @@ -42,8 +42,9 @@ config USB_DYNAMIC_MINORS
> If you are unsure about this, say N here.
>
> config USB_OTG
> - bool "OTG support"
> + tristate "OTG support"
> depends on PM
> + depends on USB && USB_GADGET
> default n
> help
> The most notable feature of
> USB OTG is support for a

With this USB_OTG will become 'm' when either USB or USB_GADGET is m
and will break if either USB or USB_GADGET is made y as all OTG core
API symbols won't be available. :)

cheers,
-roger

>>
>>>
>>> Peter
>>>
>>>> Signed-off-by: Roger Quadros <[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 1873eb3..156fd25 100644
>>>> --- a/drivers/usb/common/usb-otg-fsm.c
>>>> +++ b/drivers/usb/common/usb-otg-fsm.c
>>>> @@ -166,8 +166,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 c631dde..22d8baa 100644
>>>> --- a/include/linux/usb/otg-fsm.h
>>>> +++ b/include/linux/usb/otg-fsm.h
>>>> @@ -198,6 +198,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-07-16 02:16:36

by Peter Chen

[permalink] [raw]
Subject: Re: [PATCH v3 04/11] otg-fsm: move usb_bus_start_enum into otg-fsm->ops

On Wed, Jul 15, 2015 at 04:30:27PM +0300, Roger Quadros wrote:
> On 14/07/15 03:34, Peter Chen wrote:
> > On Mon, Jul 13, 2015 at 01:13:54PM +0300, Roger Quadros wrote:
> >> Peter,
> >>
> >> On 13/07/15 04:58, Peter Chen wrote:
> >>> On Wed, Jul 08, 2015 at 01:19:30PM +0300, Roger Quadros wrote:
> >>>> This is to prevent missing symbol build error if OTG is
> >>>> enabled (built-in) and HCD core (CONFIG_USB) is module.
> >>>>
> >>>
> >>> We may let the OTG-DRD/OTG-FSM depends on CONFIG_USB to fix it.
> >>
> >> CONFIG_OTG already depends on CONFIG_USB as it is a sub-option of
> >> CONFIG_USB. It doesn't depend on CONFIG_USB_GADGET and that can
> >> be fixed.
> >>
> >> But dependency is not the problem here. Symbols not available to
> >> OTG driver when USB/GADGET is 'm' is the problem.
> >>
> >> e.g.
> >> CONFIG_USB_OTG is always built-in.
> >> we need to work if CONFIG_USB is 'm'/'y'
> >> _and_ if CONFIG_USB_GADGET is 'm'/'y'
> >>
> >
> > below should fix this issue, but we may need to make some
> > changes for code which are defined by CONFIG_USB_OTG.
> >
> > diff --git a/drivers/usb/core/Kconfig b/drivers/usb/core/Kconfig
> > index a99c89e..5e374ad 100644
> > --- a/drivers/usb/core/Kconfig
> > +++ b/drivers/usb/core/Kconfig
> > @@ -42,8 +42,9 @@ config USB_DYNAMIC_MINORS
> > If you are unsure about this, say N here.
> >
> > config USB_OTG
> > - bool "OTG support"
> > + tristate "OTG support"
> > depends on PM
> > + depends on USB && USB_GADGET
> > default n
> > help
> > The most notable feature of
> > USB OTG is support for a
>
> With this USB_OTG will become 'm' when either USB or USB_GADGET is m
> and will break if either USB or USB_GADGET is made y as all OTG core
> API symbols won't be available. :)
>

Ok, after thinking more, seems we can't handle properly if USB_OTG as
'm', your idea that using host/gadget/fsm->ops to call hcd/gadget API
and the controller driver will defines these ops (due to it will use
hcd/gadget function) is proper way currently.

--

Best Regards,
Peter Chen

2015-07-16 10:40:19

by Roger Quadros

[permalink] [raw]
Subject: Re: [PATCH v3 04/11] otg-fsm: move usb_bus_start_enum into otg-fsm->ops

On 16/07/15 03:54, Peter Chen wrote:
> On Wed, Jul 15, 2015 at 04:30:27PM +0300, Roger Quadros wrote:
>> On 14/07/15 03:34, Peter Chen wrote:
>>> On Mon, Jul 13, 2015 at 01:13:54PM +0300, Roger Quadros wrote:
>>>> Peter,
>>>>
>>>> On 13/07/15 04:58, Peter Chen wrote:
>>>>> On Wed, Jul 08, 2015 at 01:19:30PM +0300, Roger Quadros wrote:
>>>>>> This is to prevent missing symbol build error if OTG is
>>>>>> enabled (built-in) and HCD core (CONFIG_USB) is module.
>>>>>>
>>>>>
>>>>> We may let the OTG-DRD/OTG-FSM depends on CONFIG_USB to fix it.
>>>>
>>>> CONFIG_OTG already depends on CONFIG_USB as it is a sub-option of
>>>> CONFIG_USB. It doesn't depend on CONFIG_USB_GADGET and that can
>>>> be fixed.
>>>>
>>>> But dependency is not the problem here. Symbols not available to
>>>> OTG driver when USB/GADGET is 'm' is the problem.
>>>>
>>>> e.g.
>>>> CONFIG_USB_OTG is always built-in.
>>>> we need to work if CONFIG_USB is 'm'/'y'
>>>> _and_ if CONFIG_USB_GADGET is 'm'/'y'
>>>>
>>>
>>> below should fix this issue, but we may need to make some
>>> changes for code which are defined by CONFIG_USB_OTG.
>>>
>>> diff --git a/drivers/usb/core/Kconfig b/drivers/usb/core/Kconfig
>>> index a99c89e..5e374ad 100644
>>> --- a/drivers/usb/core/Kconfig
>>> +++ b/drivers/usb/core/Kconfig
>>> @@ -42,8 +42,9 @@ config USB_DYNAMIC_MINORS
>>> If you are unsure about this, say N here.
>>>
>>> config USB_OTG
>>> - bool "OTG support"
>>> + tristate "OTG support"
>>> depends on PM
>>> + depends on USB && USB_GADGET
>>> default n
>>> help
>>> The most notable feature of
>>> USB OTG is support for a
>>
>> With this USB_OTG will become 'm' when either USB or USB_GADGET is m
>> and will break if either USB or USB_GADGET is made y as all OTG core
>> API symbols won't be available. :)
>>
>
> Ok, after thinking more, seems we can't handle properly if USB_OTG as
> 'm', your idea that using host/gadget/fsm->ops to call hcd/gadget API
> and the controller driver will defines these ops (due to it will use
> hcd/gadget function) is proper way currently.
>

Can I take this as your Ack for this patch? :)

cheers,
-roger

2015-07-16 18:30:03

by Andrew Bresticker

[permalink] [raw]
Subject: Re: [PATCH v3 00/11] USB: OTG/DRD Core functionality

Hi Roger,

On Wed, Jul 15, 2015 at 6:26 AM, Roger Quadros <[email protected]> wrote:
> Hi Andrew,
>
> On 13/07/15 22:14, Andrew Bresticker wrote:
>> Hi Roger,
>>
>> On Wed, Jul 8, 2015 at 3:19 AM, Roger Quadros <[email protected]> wrote:
>>> Usage model:
>>> -----------
>>>
>>> - The OTG controller device is assumed to be the parent of
>>> the host and gadget controller. It must call usb_otg_register()
>>> before populating the host and gadget devices so that the OTG
>>> core is aware that it is an OTG device before the host & gadget
>>> register. The OTG controller must provide struct otg_fsm_ops *
>>> which will be called by the OTG core depending on OTG bus state.
>>
>> I'm wondering if the requirement that the OTG controller be the parent
>> of the USB host/device-controllers makes sense. For some context, I'm
>> working on adding dual-role support for Tegra210, specifically on a
>> system with USB Type-C. On Tegra, the USB host-controller and USB
>> device-controller are two separate IP blocks (XUSB host and XUSB
>> device) with another, separate, IP block (XUSB padctl) for the USB PHY
>> and OTG support. In the non-Type-C case, your OTG framework could
>> work well, though it's debatable as to whether or not the XUSB padctl
>> device should be a parent to the XUSB host/device-controller devices
>> (currently it isn't - it's just a PHY provider). But in the Type-C
>> case, it's an off-chip embedded controller that determines the
>> dual-role status of the Type-C port, so the above requirement doesn't
>> make sense at all.
>>
>> My idea was to have the OTG/DRD controller explicitly specify its host
>> and device controllers, so in DT, something like:
>>
>> otg-controller {
>> ...
>> device-controller = <&usb_device>;
>> host-controller = <&usb_host>;
>> ...
>> };
>>
>> usb_device: usb-device@.... {
>> ...
>> };
>>
>> usb_host: usb-host@... {
>> ...
>> };
>>
>> What do you think?
>
> I agree that we need to support your use case but how to do it
> is not yet clear to me.
>
> In your above example the otg controller knows what are the host
> and gadget controllers but the host/gadget devices don't
> know who is their otg controller.
>
> So the problem is that when usb_otg_register_hcd/gadget() is called
> we need to get a handle to the otg controller.
>
> One solution I see is to iterate over the registered otg_controller_list
> and check if we match the host/gadget controller in there.
>
> Then there is also a possibility that host/gadget controllers get
> registered before the OTG controller. Then we can't know for sure if
> the host/gadget controller was meant for dual-role operation or not
> and it will resort to single role operation.
>
> Any idea to prevent the above situation?
>
> Maybe we need to add some logic in host/gadget cores to check if the port
> is meant for dual-role use and defer probe if OTG controller is not yet
> registered?

In the DT case, I think we could add an "otg-controller" property to
the host and gadget nodes, and in usb_otg_register_{hcd,gadget}()
check for that property and defer probe if the referenced OTG
controller has not yet been registered. Not sure how to indicate that
a host/gadget is meant for dual-role operation on non-DT platforms
though.

Thanks,
Andrew

2015-07-17 01:38:01

by Peter Chen

[permalink] [raw]
Subject: Re: [PATCH v3 04/11] otg-fsm: move usb_bus_start_enum into otg-fsm->ops

On Wed, Jul 08, 2015 at 01:19:30PM +0300, Roger Quadros wrote:
> 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]>
> ---
> 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 1873eb3..156fd25 100644
> --- a/drivers/usb/common/usb-otg-fsm.c
> +++ b/drivers/usb/common/usb-otg-fsm.c
> @@ -166,8 +166,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 c631dde..22d8baa 100644
> --- a/include/linux/usb/otg-fsm.h
> +++ b/include/linux/usb/otg-fsm.h
> @@ -198,6 +198,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
>

Acked-by: Peter Chen <[email protected]>

--

Best Regards,
Peter Chen

2015-07-17 01:39:47

by Peter Chen

[permalink] [raw]
Subject: Re: [PATCH v3 05/11] usb: hcd.h: Add OTG to HCD interface

On Wed, Jul 08, 2015 at 01:19:31PM +0300, Roger Quadros wrote:
> 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]>
> ---
> 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 c9aa779..4108288 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
>

Reviewed-by: Peter Chen <[email protected]>

--

Best Regards,
Peter Chen

2015-07-17 01:40:49

by Peter Chen

[permalink] [raw]
Subject: Re: [PATCH v3 06/11] usb: gadget.h: Add OTG to gadget interface

On Wed, Jul 08, 2015 at 01:19:32PM +0300, Roger Quadros wrote:
> 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]>
> ---
> 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 4f3dfb7..0b4b341 100644
> --- a/include/linux/usb/gadget.h
> +++ b/include/linux/usb/gadget.h
> @@ -887,6 +887,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
>

Reviewed-by: Peter Chen <[email protected]>

--

Best Regards,
Peter Chen

2015-07-17 08:58:07

by Li Jun

[permalink] [raw]
Subject: Re: [PATCH v3 07/11] usb: otg: add OTG core

Hi, Roger

On Wed, Jul 08, 2015 at 01:19:33PM +0300, Roger Quadros wrote:
> 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 | 768 +++++++++++++++++++++++++++++++++++++++++++
> drivers/usb/common/usb-otg.h | 71 ++++
> drivers/usb/core/Kconfig | 11 +-
> include/linux/usb/otg.h | 91 ++++-
> 8 files changed, 930 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 8133cef..b21278e 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -10640,12 +10640,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..0379034
> --- /dev/null
> +++ b/drivers/usb/common/usb-otg.c
> @@ -0,0 +1,768 @@
> +/**
> + * 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/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"
> +
> +/* to link timer with callback data */
> +struct otg_timer {
> + struct hrtimer timer;
> + ktime_t timeout;
> + /* callback data */
> + int *timeout_bit;
> + struct otg_data *otgd;
> +};
> +
> +struct otg_hcd {
> + struct usb_hcd *hcd;
> + unsigned int irqnum;
> + unsigned long irqflags;
> + struct otg_hcd_ops *ops;
> +};
> +
> +struct otg_data {
> + struct device *dev; /* HCD & GCD's parent device */
> +
> + struct otg_fsm fsm;
> + /* HCD, GCD and usb_otg_state are present in otg_fsm->otg
> + * HCD is bus_to_hcd(fsm->otg->host)
> + * GCD is fsm->otg->gadget
> + */
> + struct otg_fsm_ops fsm_ops; /* private copy for override */
> + struct usb_otg otg; /* allocator for fsm->otg */
> +
> + struct otg_hcd primary_hcd;
> + struct otg_hcd shared_hcd;
> +
> + struct otg_gadget_ops *gadget_ops; /* interface to gadget f/w */
> +
> + /* saved hooks to OTG device */
> + int (*start_host)(struct otg_fsm *fsm, int on);
> + int (*start_gadget)(struct otg_fsm *fsm, int on);
> +
> + struct list_head list;
> +
> + struct work_struct work; /* OTG FSM work */
> + struct workqueue_struct *wq;
> +
> + struct otg_timer timers[NUM_OTG_FSM_TIMERS];
> +
> + bool fsm_running;
> + /* use otg->fsm.lock for serializing access */
> +};
> +
> +/* OTG device list */
> +LIST_HEAD(otg_list);
> +static DEFINE_MUTEX(otg_list_mutex);
> +
> +static int usb_otg_hcd_is_primary_hcd(struct usb_hcd *hcd)
> +{
> + if (!hcd->primary_hcd)
> + return 1;
> + return hcd == hcd->primary_hcd;
> +}
> +
> +/**
> + * check if device is in our OTG list and return
> + * otg_data, else NULL.
> + *
> + * otg_list_mutex must be held.
> + */
> +static struct otg_data *usb_otg_device_get_otgd(struct device *parent_dev)
> +{
> + struct otg_data *otgd;
> +
> + list_for_each_entry(otgd, &otg_list, list) {
> + if (otgd->dev == parent_dev)
> + return otgd;
> + }
> +
> + return NULL;
> +}
> +
> +/**
> + * 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 otg_data *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 otg_data *otgd)
> +{
> + struct otg_fsm *fsm = &otgd->fsm;
> +
> + 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);
> +
> + otg_timer_init(A_WAIT_ENUM, otgd, set_tmout, TB_SRP_FAIL, NULL);

2 timers are missing: B_DATA_PLS, B_SSEND_SRP.

> +}
> +
> +/**
> + * OTG FSM ops function to add timer
> + */
> +static void usb_otg_add_timer(struct otg_fsm *fsm, enum otg_fsm_timer id)
> +{
> + struct otg_data *otgd = container_of(fsm, struct otg_data, 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 otg_data *otgd = container_of(fsm, struct otg_data, fsm);
> + struct hrtimer *timer = &otgd->timers[id].timer;
> +
> + hrtimer_cancel(timer);
> +}
> +
> +/**
> + * OTG FSM ops function to start/stop host
> + */
> +static int usb_otg_start_host(struct otg_fsm *fsm, int on)
> +{
> + struct otg_data *otgd = container_of(fsm, struct otg_data, 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) {
> + /* OTG device operations */
> + if (otgd->start_host)
> + otgd->start_host(fsm, 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);
> +
> + /* OTG device operations */
> + if (otgd->start_host)
> + otgd->start_host(fsm, on);
> + }
> +
> + return 0;
> +}

I do not see much benefit by this override function, usb_add/remove_hcd
can be simply included by controller's start_host function, also there
maybe some additional operations after usb_add_hcd, but this override
function limit usb_add_hcd() is the last step.

Maybe your intention is to make usb_add_hcd is the only operation required
to start host, so ideally controller driver need not define its start_host
routine for this otg ops, I am not sure if this can work for different otg
platforms. If the shared code is only usb_add/remove_hcd(), maybe leave this
ops defined by controller driver can make core code simple and give flexibility
to controller drivers.

> +
> +/**
> + * OTG FSM ops function to start/stop gadget
> + */
> +static int usb_otg_start_gadget(struct otg_fsm *fsm, int on)
> +{
> + struct otg_data *otgd = container_of(fsm, struct otg_data, 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) {
> + /* OTG device operations */
> + if (otgd->start_gadget)
> + otgd->start_gadget(fsm, on);
> +
> + otgd->gadget_ops->start(fsm->otg->gadget);
> + } else {
> + otgd->gadget_ops->stop(fsm->otg->gadget);
> +
> + /* OTG device operations */
> + if (otgd->start_gadget)
> + otgd->start_gadget(fsm, on);
> + }
> +
> + return 0;
> +}
> +
> +/**
> + * OTG FSM work function
> + */
> +static void usb_otg_work(struct work_struct *work)
> +{
> + struct otg_data *otgd = container_of(work, struct otg_data, work);
> +
> + otg_statemachine(&otgd->fsm);

Need consider runtime pm, or you want to rely on controller driver take
care of it?

> +}
> +
> +/**
> + * usb_otg_register() - Register the OTG device to OTG core
> + * @parent_device: parent device of Host & Gadget controllers.
> + * @otg_fsm_ops: otg state machine ops.
> + *
> + * Register parent device that contains both HCD and GCD into
> + * the USB OTG core. HCD and GCD will be prevented from starting
> + * till both are available for use.
> + *
> + * Return: struct otg_fsm * if success, NULL if error.
> + */
> +struct otg_fsm *usb_otg_register(struct device *parent_dev,
> + struct otg_fsm_ops *fsm_ops)
> +{
> + struct otg_data *otgd;
> + int ret = 0;
> +
> + if (!parent_dev || !fsm_ops)
> + return ERR_PTR(-EINVAL);
> +
> + /* already in list? */
> + mutex_lock(&otg_list_mutex);
> + if (usb_otg_device_get_otgd(parent_dev)) {
> + dev_err(parent_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 = parent_dev;
> + INIT_WORK(&otgd->work, usb_otg_work);
> + otgd->wq = create_singlethread_workqueue("usb_otg");
> + if (!otgd->wq) {
> + dev_err(parent_dev, "otg: %s: can't create workqueue\n",
> + __func__);
> + ret = -ENODEV;
> + goto err_wq;
> + }
> +
> + usb_otg_init_timers(otgd);
> +
> + /* save original start host/gadget ops */
> + otgd->start_host = fsm_ops->start_host;
> + otgd->start_gadget = fsm_ops->start_gadget;
> + /* create copy of original ops */
> + otgd->fsm_ops = *fsm_ops;
> + /* override ops */
> + otgd->fsm_ops.start_host = usb_otg_start_host;
> + otgd->fsm_ops.start_gadget = usb_otg_start_gadget;
> + /* FIXME: we ignore caller's timer ops */
> + otgd->fsm_ops.add_timer = usb_otg_add_timer;
> + otgd->fsm_ops.del_timer = usb_otg_del_timer;
> + /* set otg ops */
> + otgd->fsm.ops = &otgd->fsm_ops;
> + otgd->fsm.otg = &otgd->otg;
> +
> + mutex_init(&otgd->fsm.lock);
> +
> + list_add_tail(&otgd->list, &otg_list);
> + mutex_unlock(&otg_list_mutex);
> + 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
> + * @parent_device: parent device of Host & Gadget controllers.
> + *
> + * Unregister parent OTG deviced from USB OTG core.
> + * Prevents unregistering till both Host and Gadget controllers
> + * have unregistered from the OTG core.
> + *
> + * Return: 0 on success, error value otherwise.
> + */
> +int usb_otg_unregister(struct device *parent_dev)
> +{
> + struct otg_data *otgd;
> +
> + mutex_lock(&otg_list_mutex);
> + otgd = usb_otg_device_get_otgd(parent_dev);
> + if (!otgd) {
> + dev_err(parent_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(parent_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 otg_data *otgd = container_of(fsm, struct otg_data, 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;
> + }
> +

This cannot work, fsm->otg->host is set in usb_otg_register_hcd(), which is
called by usb_add_hcd(), but usb_add_hcd() should be called only if otg fsm
already started to some A-device state, deadlock.

> + 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 otg_data *otgd = container_of(fsm, struct otg_data, 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 otg_data *otgd = container_of(fsm, struct otg_data, 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 otg_data *otgd;
> +
> + mutex_lock(&otg_list_mutex);
> + otgd = usb_otg_device_get_otgd(hcd_gcd_device->parent);
> + if (!otgd) {
> + dev_err(hcd_gcd_device, "otg: %s: invalid host/gadget device\n",
> + __func__);
> + mutex_unlock(&otg_list_mutex);
> + return -ENODEV;
> + }
> +
> + mutex_unlock(&otg_list_mutex);
> + 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 otg_data *otgd;
> + struct device *otg_dev = hcd->self.controller->parent;
> +
> + mutex_lock(&otg_list_mutex);
> + otgd = usb_otg_device_get_otgd(otg_dev);
> + if (!otgd) {
> + dev_dbg(otg_dev, "otg: %s: device not registered to otg core\n",
> + __func__);
> + mutex_unlock(&otg_list_mutex);
> + return -EINVAL;
> + }
> +
> + mutex_unlock(&otg_list_mutex);
> + /* 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);

usb_otg_register_hcd() is called before usb_otg_add_hcd(), start fsm on
this point can make sense since hcd has not been added?

> + } 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 otg_data *otgd;
> + struct usb_bus *bus = hcd_to_bus(hcd);
> + struct device *otg_dev = bus->controller->parent;
> +
> + mutex_lock(&otg_list_mutex);
> + otgd = usb_otg_device_get_otgd(otg_dev);
> + if (!otgd) {
> + dev_err(otg_dev, "otg: %s: device not registered to otg core\n",
> + __func__);
> + mutex_unlock(&otg_list_mutex);
> + return -EINVAL;
> + }
> +
> + mutex_unlock(&otg_list_mutex);
> +
> + 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(bus->controller));
> + } else if (hcd == otgd->shared_hcd.hcd) {
> + otgd->shared_hcd.hcd = NULL;
> + dev_info(otg_dev, "otg: shared host %s unregistered\n",
> + dev_name(bus->controller));
> + } else {
> + dev_err(otg_dev, "otg: host %s wasn't registered with otg\n",
> + dev_name(bus->controller));
> + 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 otg_data *otgd;
> + struct device *otg_dev = gadget->dev.parent;
> +
> + mutex_lock(&otg_list_mutex);
> + otgd = usb_otg_device_get_otgd(otg_dev);
> + if (!otgd) {
> + dev_err(otg_dev, "otg: %s: device not registered to otg core\n",
> + __func__);
> + mutex_unlock(&otg_list_mutex);
> + return -EINVAL;
> + }
> +
> + mutex_unlock(&otg_list_mutex);
> +
> + 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 otg_data *otgd;
> + struct device *otg_dev = gadget->dev.parent;
> +
> + mutex_lock(&otg_list_mutex);
> + otgd = usb_otg_device_get_otgd(otg_dev);
> + if (!otgd) {
> + dev_err(otg_dev, "otg: %s: device not registered to otg core\n",
> + __func__);
> + mutex_unlock(&otg_list_mutex);
> + return -EINVAL;
> + }
> +
> + mutex_unlock(&otg_list_mutex);
> +
> + 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 otg_data *otgd = container_of(fsm, struct otg_data, 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)
> +

All otg timer timeout value should be in some *range* defined by otg spec,
not some specific value, I don't think one specific value can meet all otg
platforms, so we need find a way to make those value can be configured by
controller drivers.

> +#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 52661c5..ce6f8d8 100644
> --- a/include/linux/usb/otg.h
> +++ b/include/linux/usb/otg.h
> @@ -10,6 +10,11 @@
> #define __LINUX_USB_OTG_H
>
> #include <linux/phy/phy.h>
> +#include <linux/device.h>
> +#include <linux/usb.h>
> +#include <linux/usb/hcd.h>
> +#include <linux/usb/gadget.h>
> +#include <linux/usb/otg-fsm.h>
> #include <linux/usb/phy.h>
>
> struct usb_otg {
> @@ -23,6 +28,7 @@ struct usb_otg {
>
> enum usb_otg_state state;
>
> +/*------------- deprecated interface -----------------------------*/
> /* bind/unbind the host controller */
> int (*set_host)(struct usb_otg *otg, struct usb_bus *host);
>
> @@ -38,11 +44,85 @@ struct usb_otg {
>
> /* start or continue HNP role switch */
> int (*start_hnp)(struct usb_otg *otg);
> -
> +/*---------------------------------------------------------------*/
> };
>
> 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 *parent_dev,
> + struct otg_fsm_ops *fsm_ops);
> +int usb_otg_unregister(struct device *parent_dev);
> +int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
> + unsigned long irqflags, struct otg_hcd_ops *ops);
> +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);
> +
> +#else /* CONFIG_USB_OTG */
> +
> +static inline struct otg_fsm *usb_otg_register(struct device *parent_dev,
> + struct otg_fsm_ops *fsm_ops)
> +{
> + return ERR_PTR(-ENOSYS);
> +}
> +
> +static inline int usb_otg_unregister(struct device *parent_dev)
> +{
> + return -ENOSYS;
> +}
> +
> +static inline int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
> + unsigned long irqflags,
> + struct otg_hcd_ops *ops)
> +{
> + return -ENOSYS;
> +}
> +
> +static inline int usb_otg_unregister_hcd(struct usb_hcd *hcd)
> +{
> + return -ENOSYS;
> +}
> +
> +static inline int usb_otg_register_gadget(struct usb_gadget *gadget,
> + struct otg_gadget_ops *ops)
> +{
> + return -ENOSYS;
> +}
> +
> +static inline int usb_otg_unregister_gadget(struct usb_gadget *gadget)
> +{
> + return -ENOSYS;
> +}
> +
> +static inline void usb_otg_sync_inputs(struct otg_fsm *fsm)
> +{
> +}
> +
> +static inline int usb_otg_kick_fsm(struct device *hcd_gcd_device)
> +{
> + return -ENOSYS;
> +}
> +
> +static inline struct device *usb_otg_fsm_to_dev(struct otg_fsm *fsm)
> +{
> + return NULL;
> +}
> +
> +#endif /* CONFIG_USB_OTG */
> +
> +/*------------- deprecated interface -----------------------------*/
> /* Context: can sleep */
> static inline int
> otg_start_hnp(struct usb_otg *otg)
> @@ -94,14 +174,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
>

I assume I will have more comments after try more.

Li Jun

2015-07-17 09:23:54

by Li Jun

[permalink] [raw]
Subject: Re: [PATCH v3 10/11] usb: otg: Add dual-role device (DRD) support

Hi,

On Wed, Jul 08, 2015 at 01:19:36PM +0300, Roger Quadros wrote:
> DRD mode is a reduced functionality OTG mode. In this mode
> we don't support SRP, HNP and dynamic role-swap.
>
> In DRD operation, the controller mode (Host or Peripheral)
> is decided based on the ID pin status. Once a cable plug (Type-A
> or Type-B) is attached the controller selects the state
> and doesn't change till the cable in unplugged and a different
> cable type is inserted.
>
> As we don't need most of the complex OTG states and OTG timers
> we implement a lean DRD state machine in usb-otg.c.
> The DRD state machine is only interested in 2 hardware inputs
> 'id' and 'vbus; that are still passed via the origintal struct otg_fsm.
>
> Most of the usb-otg.c functionality remains the same except
> adding a new parameter to usb_otg_register() to indicate that
> the OTG controller needs to operate in DRD mode.
>
> Signed-off-by: Roger Quadros <[email protected]>
> ---
> drivers/usb/common/usb-otg.c | 179 ++++++++++++++++++++++++++++++++++++++++---
> include/linux/usb/otg-fsm.h | 8 +-
> include/linux/usb/otg.h | 5 +-
> 3 files changed, 180 insertions(+), 12 deletions(-)
>
> diff --git a/drivers/usb/common/usb-otg.c b/drivers/usb/common/usb-otg.c
> index 1f19001..9b89f4b 100644
> --- a/drivers/usb/common/usb-otg.c
> +++ b/drivers/usb/common/usb-otg.c
> @@ -44,6 +44,7 @@ struct otg_hcd {
> struct otg_data {
> struct device *dev; /* HCD & GCD's parent device */
>
> + bool drd_only; /* Dual-role only, no OTG features */

After we introduce otg caps, we can use hnp_support to judge it it's
drd or OTG.

> struct otg_fsm fsm;
> /* HCD, GCD and usb_otg_state are present in otg_fsm->otg
> * HCD is bus_to_hcd(fsm->otg->host)
> @@ -272,20 +273,172 @@ static int usb_otg_start_gadget(struct otg_fsm *fsm, int on)
> return 0;
> }
>
> +/* Change USB protocol when there is a protocol change */
> +static int drd_set_protocol(struct otg_fsm *fsm, int protocol)
> +{
> + struct otg_data *otgd = container_of(fsm, struct otg_data, fsm);
> + int ret = 0;
> +
> + if (fsm->protocol != protocol) {
> + dev_dbg(otgd->dev, "otg: changing role fsm->protocol= %d; new protocol= %d\n",
> + fsm->protocol, protocol);
> + /* stop old protocol */
> + if (fsm->protocol == PROTO_HOST)
> + ret = otg_start_host(fsm, 0);
> + else if (fsm->protocol == PROTO_GADGET)
> + ret = otg_start_gadget(fsm, 0);
> + if (ret)
> + return ret;
> +
> + /* start new protocol */
> + if (protocol == PROTO_HOST)
> + ret = otg_start_host(fsm, 1);
> + else if (protocol == PROTO_GADGET)
> + ret = otg_start_gadget(fsm, 1);
> + if (ret)
> + return ret;
> +
> + fsm->protocol = protocol;
> + return 0;
> + }
> +
> + return 0;
> +}
> +
> +/* Called when entering a DRD state */
> +static void drd_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state)
> +{
> + struct otg_data *otgd = container_of(fsm, struct otg_data, fsm);
> +
> + if (fsm->otg->state == new_state)
> + return;
> +
> + fsm->state_changed = 1;
> + dev_dbg(otgd->dev, "otg: set state: %s\n",
> + usb_otg_state_string(new_state));
> + switch (new_state) {
> + case OTG_STATE_B_IDLE:

otg_drv_vbus(fsm, 0);

> + drd_set_protocol(fsm, PROTO_UNDEF);
> + break;
> + case OTG_STATE_B_PERIPHERAL:

otg_drv_vbus(fsm, 0);

> + drd_set_protocol(fsm, PROTO_GADGET);
> + break;
> + case OTG_STATE_A_HOST:

otg_drv_vbus(fsm, 1);

> + drd_set_protocol(fsm, PROTO_HOST);
> + break;
> + case OTG_STATE_UNDEFINED:
> + case OTG_STATE_B_SRP_INIT:
> + case OTG_STATE_B_WAIT_ACON:
> + case OTG_STATE_B_HOST:
> + case OTG_STATE_A_IDLE:
> + case OTG_STATE_A_WAIT_VRISE:
> + case OTG_STATE_A_WAIT_BCON:
> + case OTG_STATE_A_SUSPEND:
> + case OTG_STATE_A_PERIPHERAL:
> + case OTG_STATE_A_WAIT_VFALL:
> + case OTG_STATE_A_VBUS_ERR:
> + default:
> + dev_warn(otgd->dev, "%s: otg: invalid state: %s\n",
> + __func__, usb_otg_state_string(new_state));
> + break;
> + }
> +
> + fsm->otg->state = new_state;
> +}
> +
> /**
> - * OTG FSM work function
> + * DRD state change judgement
> + *
> + * For DRD we're only interested in some of the OTG states
> + * i.e. OTG_STATE_B_IDLE: both peripheral and host are stopped
> + * OTG_STATE_B_PERIPHERAL: peripheral active
> + * OTG_STATE_A_HOST: host active
> + * we're only interested in the following inputs
> + * fsm->id, fsm->vbus
> + */
> +static int drd_statemachine(struct otg_fsm *fsm)
> +{
> + struct otg_data *otgd = container_of(fsm, struct otg_data, fsm);
> + enum usb_otg_state state;
> +
> + mutex_lock(&fsm->lock);
> +
> + state = fsm->otg->state;
> +
> + switch (state) {
> + case OTG_STATE_UNDEFINED:
> + if (!fsm->id)
> + drd_set_state(fsm, OTG_STATE_A_HOST);
> + else if (fsm->id && fsm->vbus)
> + drd_set_state(fsm, OTG_STATE_B_PERIPHERAL);
> + else
> + drd_set_state(fsm, OTG_STATE_B_IDLE);
> + break;
> + case OTG_STATE_B_IDLE:
> + if (!fsm->id)
> + drd_set_state(fsm, OTG_STATE_A_HOST);
> + else if (fsm->vbus)
> + drd_set_state(fsm, OTG_STATE_B_PERIPHERAL);
> + break;
> + case OTG_STATE_B_PERIPHERAL:
> + if (!fsm->id)
> + drd_set_state(fsm, OTG_STATE_A_HOST);
> + else if (!fsm->vbus)
> + drd_set_state(fsm, OTG_STATE_B_IDLE);
> + break;
> + case OTG_STATE_A_HOST:
> + if (fsm->id && fsm->vbus)
> + drd_set_state(fsm, OTG_STATE_B_PERIPHERAL);
> + else if (fsm->id && !fsm->vbus)
> + drd_set_state(fsm, OTG_STATE_B_IDLE);
> + break;
> +
> + /* invalid states for DRD */
> + case OTG_STATE_B_SRP_INIT:
> + case OTG_STATE_B_WAIT_ACON:
> + case OTG_STATE_B_HOST:
> + case OTG_STATE_A_IDLE:
> + case OTG_STATE_A_WAIT_VRISE:
> + case OTG_STATE_A_WAIT_BCON:
> + case OTG_STATE_A_SUSPEND:
> + case OTG_STATE_A_PERIPHERAL:
> + case OTG_STATE_A_WAIT_VFALL:
> + case OTG_STATE_A_VBUS_ERR:
> + dev_err(otgd->dev, "%s: otg: invalid usb-drd state: %s\n",
> + __func__, usb_otg_state_string(state));
> + drd_set_state(fsm, OTG_STATE_UNDEFINED);
> + break;
> + }
> +
> + mutex_unlock(&fsm->lock);
> + dev_dbg(otgd->dev, "otg: quit statemachine, changed %d\n",
> + fsm->state_changed);
> +
> + return fsm->state_changed;
> +}
> +
> +/**
> + * OTG FSM/DRD work function
> */
> static void usb_otg_work(struct work_struct *work)
> {
> struct otg_data *otgd = container_of(work, struct otg_data, work);
>
> - otg_statemachine(&otgd->fsm);
> + /* OTG state machine */
> + if (!otgd->drd_only) {
> + otg_statemachine(&otgd->fsm);
> + return;
> + }
> +
> + /* DRD state machine */
> + drd_statemachine(&otgd->fsm);
> }
>
> /**
> * usb_otg_register() - Register the OTG device to OTG core
> * @parent_device: parent device of Host & Gadget controllers.
> * @otg_fsm_ops: otg state machine ops.
> + * @drd_only: dual-role only. no OTG features.
> *
> * Register parent device that contains both HCD and GCD into
> * the USB OTG core. HCD and GCD will be prevented from starting
> @@ -294,7 +447,8 @@ static void usb_otg_work(struct work_struct *work)
> * Return: struct otg_fsm * if success, NULL if error.
> */
> struct otg_fsm *usb_otg_register(struct device *parent_dev,
> - struct otg_fsm_ops *fsm_ops)
> + struct otg_fsm_ops *fsm_ops,
> + bool drd_only)
> {
> struct otg_data *otgd;
> int ret = 0;
> @@ -328,7 +482,15 @@ struct otg_fsm *usb_otg_register(struct device *parent_dev,
> goto err_wq;
> }
>
> - usb_otg_init_timers(otgd);
> + otgd->drd_only = drd_only;
> + /* For DRD mode we don't need OTG timers */
> + if (!drd_only) {
> + usb_otg_init_timers(otgd);
> +
> + /* FIXME: we ignore caller's timer ops */
> + otgd->fsm_ops.add_timer = usb_otg_add_timer;
> + otgd->fsm_ops.del_timer = usb_otg_del_timer;
> + }
>
> /* save original start host/gadget ops */
> otgd->start_host = fsm_ops->start_host;
> @@ -338,9 +500,6 @@ struct otg_fsm *usb_otg_register(struct device *parent_dev,
> /* override ops */
> otgd->fsm_ops.start_host = usb_otg_start_host;
> otgd->fsm_ops.start_gadget = usb_otg_start_gadget;
> - /* FIXME: we ignore caller's timer ops */
> - otgd->fsm_ops.add_timer = usb_otg_add_timer;
> - otgd->fsm_ops.del_timer = usb_otg_del_timer;
> /* set otg ops */
> otgd->fsm.ops = &otgd->fsm_ops;
> otgd->fsm.otg = &otgd->otg;
> @@ -443,8 +602,10 @@ static void usb_otg_stop_fsm(struct otg_fsm *fsm)
> otgd->fsm_running = false;
>
> /* Stop state machine / timers */
> - for (i = 0; i < ARRAY_SIZE(otgd->timers); i++)
> - hrtimer_cancel(&otgd->timers[i].timer);
> + if (!otgd->drd_only) {
> + for (i = 0; i < ARRAY_SIZE(otgd->timers); i++)
> + hrtimer_cancel(&otgd->timers[i].timer);
> + }
>
> flush_workqueue(otgd->wq);
> fsm->otg->state = OTG_STATE_UNDEFINED;
> diff --git a/include/linux/usb/otg-fsm.h b/include/linux/usb/otg-fsm.h
> index 22d8baa..ae9c30a 100644
> --- a/include/linux/usb/otg-fsm.h
> +++ b/include/linux/usb/otg-fsm.h
> @@ -48,6 +48,11 @@ enum otg_fsm_timer {
> /**
> * struct otg_fsm - OTG state machine according to the OTG spec
> *
> + * DRD mode hardware Inputs
> + *
> + * @id: TRUE for B-device, FALSE for A-device.
> + * @vbus: VBUS voltage in regulation.
> + *
> * OTG hardware Inputs
> *
> * Common inputs for A and B device
> @@ -122,7 +127,8 @@ enum otg_fsm_timer {
> */
> struct otg_fsm {
> /* Input */
> - int id;
> + int id; /* DRD + OTG */
> + int vbus; /* DRD only */
> int adp_change;
> int power_up;
> int a_srp_det;
> diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h
> index ce6f8d8..1086a0b 100644
> --- a/include/linux/usb/otg.h
> +++ b/include/linux/usb/otg.h
> @@ -58,7 +58,7 @@ enum usb_dr_mode {
>
> #if IS_ENABLED(CONFIG_USB_OTG)
> struct otg_fsm *usb_otg_register(struct device *parent_dev,
> - struct otg_fsm_ops *fsm_ops);
> + struct otg_fsm_ops *fsm_ops, bool drd_only);
> int usb_otg_unregister(struct device *parent_dev);
> int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
> unsigned long irqflags, struct otg_hcd_ops *ops);
> @@ -73,7 +73,8 @@ struct device *usb_otg_fsm_to_dev(struct otg_fsm *fsm);
> #else /* CONFIG_USB_OTG */
>
> static inline struct otg_fsm *usb_otg_register(struct device *parent_dev,
> - struct otg_fsm_ops *fsm_ops)
> + struct otg_fsm_ops *fsm_ops,
> + bool drd_only)
> {
> return ERR_PTR(-ENOSYS);
> }
> --
> 2.1.4
>

2015-07-17 10:11:38

by Li Jun

[permalink] [raw]
Subject: Re: [PATCH v3 10/11] usb: otg: Add dual-role device (DRD) support

On Wed, Jul 08, 2015 at 01:19:36PM +0300, Roger Quadros wrote:

[...]

> struct otg_fsm *usb_otg_register(struct device *parent_dev,
> - struct otg_fsm_ops *fsm_ops)
> + struct otg_fsm_ops *fsm_ops,
> + bool drd_only)
> {
> struct otg_data *otgd;
> int ret = 0;
> @@ -328,7 +482,15 @@ struct otg_fsm *usb_otg_register(struct device *parent_dev,
> goto err_wq;
> }
>
> - usb_otg_init_timers(otgd);
> + otgd->drd_only = drd_only;
> + /* For DRD mode we don't need OTG timers */
> + if (!drd_only) {
> + usb_otg_init_timers(otgd);
> +
> + /* FIXME: we ignore caller's timer ops */
> + otgd->fsm_ops.add_timer = usb_otg_add_timer;
> + otgd->fsm_ops.del_timer = usb_otg_del_timer;
> + }
>
> /* save original start host/gadget ops */
> otgd->start_host = fsm_ops->start_host;
> @@ -338,9 +500,6 @@ struct otg_fsm *usb_otg_register(struct device *parent_dev,

Your above override will be override back to be fsm_ops's by below copy:

/* create copy of original ops */
otgd->fsm_ops = *fsm_ops;

So add/del_timer must be override after the copy.

> /* override ops */
> otgd->fsm_ops.start_host = usb_otg_start_host;
> otgd->fsm_ops.start_gadget = usb_otg_start_gadget;
> - /* FIXME: we ignore caller's timer ops */
> - otgd->fsm_ops.add_timer = usb_otg_add_timer;
> - otgd->fsm_ops.del_timer = usb_otg_del_timer;
> /* set otg ops */
> otgd->fsm.ops = &otgd->fsm_ops;
> otgd->fsm.otg = &otgd->otg;
> @@ -443,8 +602,10 @@ static void usb_otg_stop_fsm(struct otg_fsm *fsm)
> otgd->fsm_running = false;
>
> /* Stop state machine / timers */
> - for (i = 0; i < ARRAY_SIZE(otgd->timers); i++)
> - hrtimer_cancel(&otgd->timers[i].timer);
> + if (!otgd->drd_only) {
> + for (i = 0; i < ARRAY_SIZE(otgd->timers); i++)
> + hrtimer_cancel(&otgd->timers[i].timer);
> + }
>
> flush_workqueue(otgd->wq);
> fsm->otg->state = OTG_STATE_UNDEFINED;
> diff --git a/include/linux/usb/otg-fsm.h b/include/linux/usb/otg-fsm.h
> index 22d8baa..ae9c30a 100644
> --- a/include/linux/usb/otg-fsm.h
> +++ b/include/linux/usb/otg-fsm.h
> @@ -48,6 +48,11 @@ enum otg_fsm_timer {
> /**
> * struct otg_fsm - OTG state machine according to the OTG spec
> *
> + * DRD mode hardware Inputs
> + *
> + * @id: TRUE for B-device, FALSE for A-device.
> + * @vbus: VBUS voltage in regulation.
> + *
> * OTG hardware Inputs
> *
> * Common inputs for A and B device
> @@ -122,7 +127,8 @@ enum otg_fsm_timer {
> */
> struct otg_fsm {
> /* Input */
> - int id;
> + int id; /* DRD + OTG */
> + int vbus; /* DRD only */

Existing b_sess_vld can be also used for drd only case, no need create
a new flag.

> int adp_change;
> int power_up;
> int a_srp_det;
> diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h
> index ce6f8d8..1086a0b 100644
> --- a/include/linux/usb/otg.h
> +++ b/include/linux/usb/otg.h
> @@ -58,7 +58,7 @@ enum usb_dr_mode {
>
> #if IS_ENABLED(CONFIG_USB_OTG)
> struct otg_fsm *usb_otg_register(struct device *parent_dev,
> - struct otg_fsm_ops *fsm_ops);
> + struct otg_fsm_ops *fsm_ops, bool drd_only);
> int usb_otg_unregister(struct device *parent_dev);
> int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
> unsigned long irqflags, struct otg_hcd_ops *ops);
> @@ -73,7 +73,8 @@ struct device *usb_otg_fsm_to_dev(struct otg_fsm *fsm);
> #else /* CONFIG_USB_OTG */
>
> static inline struct otg_fsm *usb_otg_register(struct device *parent_dev,
> - struct otg_fsm_ops *fsm_ops)
> + struct otg_fsm_ops *fsm_ops,
> + bool drd_only)
> {
> return ERR_PTR(-ENOSYS);
> }
> --
> 2.1.4
>

2015-07-17 10:34:44

by Roger Quadros

[permalink] [raw]
Subject: Re: [PATCH v3 00/11] USB: OTG/DRD Core functionality

On 16/07/15 21:29, Andrew Bresticker wrote:
> Hi Roger,
>
> On Wed, Jul 15, 2015 at 6:26 AM, Roger Quadros <[email protected]> wrote:
>> Hi Andrew,
>>
>> On 13/07/15 22:14, Andrew Bresticker wrote:
>>> Hi Roger,
>>>
>>> On Wed, Jul 8, 2015 at 3:19 AM, Roger Quadros <[email protected]> wrote:
>>>> Usage model:
>>>> -----------
>>>>
>>>> - The OTG controller device is assumed to be the parent of
>>>> the host and gadget controller. It must call usb_otg_register()
>>>> before populating the host and gadget devices so that the OTG
>>>> core is aware that it is an OTG device before the host & gadget
>>>> register. The OTG controller must provide struct otg_fsm_ops *
>>>> which will be called by the OTG core depending on OTG bus state.
>>>
>>> I'm wondering if the requirement that the OTG controller be the parent
>>> of the USB host/device-controllers makes sense. For some context, I'm
>>> working on adding dual-role support for Tegra210, specifically on a
>>> system with USB Type-C. On Tegra, the USB host-controller and USB
>>> device-controller are two separate IP blocks (XUSB host and XUSB
>>> device) with another, separate, IP block (XUSB padctl) for the USB PHY
>>> and OTG support. In the non-Type-C case, your OTG framework could
>>> work well, though it's debatable as to whether or not the XUSB padctl
>>> device should be a parent to the XUSB host/device-controller devices
>>> (currently it isn't - it's just a PHY provider). But in the Type-C
>>> case, it's an off-chip embedded controller that determines the
>>> dual-role status of the Type-C port, so the above requirement doesn't
>>> make sense at all.
>>>
>>> My idea was to have the OTG/DRD controller explicitly specify its host
>>> and device controllers, so in DT, something like:
>>>
>>> otg-controller {
>>> ...
>>> device-controller = <&usb_device>;
>>> host-controller = <&usb_host>;
>>> ...
>>> };
>>>
>>> usb_device: usb-device@.... {
>>> ...
>>> };
>>>
>>> usb_host: usb-host@... {
>>> ...
>>> };
>>>
>>> What do you think?
>>
>> I agree that we need to support your use case but how to do it
>> is not yet clear to me.
>>
>> In your above example the otg controller knows what are the host
>> and gadget controllers but the host/gadget devices don't
>> know who is their otg controller.
>>
>> So the problem is that when usb_otg_register_hcd/gadget() is called
>> we need to get a handle to the otg controller.
>>
>> One solution I see is to iterate over the registered otg_controller_list
>> and check if we match the host/gadget controller in there.
>>
>> Then there is also a possibility that host/gadget controllers get
>> registered before the OTG controller. Then we can't know for sure if
>> the host/gadget controller was meant for dual-role operation or not
>> and it will resort to single role operation.
>>
>> Any idea to prevent the above situation?
>>
>> Maybe we need to add some logic in host/gadget cores to check if the port
>> is meant for dual-role use and defer probe if OTG controller is not yet
>> registered?
>
> In the DT case, I think we could add an "otg-controller" property to
> the host and gadget nodes, and in usb_otg_register_{hcd,gadget}()
> check for that property and defer probe if the referenced OTG
> controller has not yet been registered. Not sure how to indicate that
> a host/gadget is meant for dual-role operation on non-DT platforms
> though.
>

Sounds fine for DT case.

For non DT case I don't see any other way to connect the OTG controller
to the host/gadget controllers other than the parent child relationship.

Maybe for non DT case we can still have that constraint.
We can have this constraint regardless so that DT users who fit
in that model don't need to add 'otg-controller' property.

cheers,
-roger

2015-07-17 10:41:45

by Roger Quadros

[permalink] [raw]
Subject: Re: [PATCH v3 10/11] usb: otg: Add dual-role device (DRD) support

On 17/07/15 11:14, Li Jun wrote:
> Hi,
>
> On Wed, Jul 08, 2015 at 01:19:36PM +0300, Roger Quadros wrote:
>> DRD mode is a reduced functionality OTG mode. In this mode
>> we don't support SRP, HNP and dynamic role-swap.
>>
>> In DRD operation, the controller mode (Host or Peripheral)
>> is decided based on the ID pin status. Once a cable plug (Type-A
>> or Type-B) is attached the controller selects the state
>> and doesn't change till the cable in unplugged and a different
>> cable type is inserted.
>>
>> As we don't need most of the complex OTG states and OTG timers
>> we implement a lean DRD state machine in usb-otg.c.
>> The DRD state machine is only interested in 2 hardware inputs
>> 'id' and 'vbus; that are still passed via the origintal struct otg_fsm.
>>
>> Most of the usb-otg.c functionality remains the same except
>> adding a new parameter to usb_otg_register() to indicate that
>> the OTG controller needs to operate in DRD mode.
>>
>> Signed-off-by: Roger Quadros <[email protected]>
>> ---
>> drivers/usb/common/usb-otg.c | 179 ++++++++++++++++++++++++++++++++++++++++---
>> include/linux/usb/otg-fsm.h | 8 +-
>> include/linux/usb/otg.h | 5 +-
>> 3 files changed, 180 insertions(+), 12 deletions(-)
>>
>> diff --git a/drivers/usb/common/usb-otg.c b/drivers/usb/common/usb-otg.c
>> index 1f19001..9b89f4b 100644
>> --- a/drivers/usb/common/usb-otg.c
>> +++ b/drivers/usb/common/usb-otg.c
>> @@ -44,6 +44,7 @@ struct otg_hcd {
>> struct otg_data {
>> struct device *dev; /* HCD & GCD's parent device */
>>
>> + bool drd_only; /* Dual-role only, no OTG features */
>
> After we introduce otg caps, we can use hnp_support to judge it it's
> drd or OTG.

Yes. I will rebase this series on top of your otg_caps patches
and incorporate it.

>
>> struct otg_fsm fsm;
>> /* HCD, GCD and usb_otg_state are present in otg_fsm->otg
>> * HCD is bus_to_hcd(fsm->otg->host)
>> @@ -272,20 +273,172 @@ static int usb_otg_start_gadget(struct otg_fsm *fsm, int on)
>> return 0;
>> }
>>
>> +/* Change USB protocol when there is a protocol change */
>> +static int drd_set_protocol(struct otg_fsm *fsm, int protocol)
>> +{
>> + struct otg_data *otgd = container_of(fsm, struct otg_data, fsm);
>> + int ret = 0;
>> +
>> + if (fsm->protocol != protocol) {
>> + dev_dbg(otgd->dev, "otg: changing role fsm->protocol= %d; new protocol= %d\n",
>> + fsm->protocol, protocol);
>> + /* stop old protocol */
>> + if (fsm->protocol == PROTO_HOST)
>> + ret = otg_start_host(fsm, 0);
>> + else if (fsm->protocol == PROTO_GADGET)
>> + ret = otg_start_gadget(fsm, 0);
>> + if (ret)
>> + return ret;
>> +
>> + /* start new protocol */
>> + if (protocol == PROTO_HOST)
>> + ret = otg_start_host(fsm, 1);
>> + else if (protocol == PROTO_GADGET)
>> + ret = otg_start_gadget(fsm, 1);
>> + if (ret)
>> + return ret;
>> +
>> + fsm->protocol = protocol;
>> + return 0;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +/* Called when entering a DRD state */
>> +static void drd_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state)
>> +{
>> + struct otg_data *otgd = container_of(fsm, struct otg_data, fsm);
>> +
>> + if (fsm->otg->state == new_state)
>> + return;
>> +
>> + fsm->state_changed = 1;
>> + dev_dbg(otgd->dev, "otg: set state: %s\n",
>> + usb_otg_state_string(new_state));
>> + switch (new_state) {
>> + case OTG_STATE_B_IDLE:
>
> otg_drv_vbus(fsm, 0);
>
>> + drd_set_protocol(fsm, PROTO_UNDEF);
>> + break;
>> + case OTG_STATE_B_PERIPHERAL:
>
> otg_drv_vbus(fsm, 0);
>
>> + drd_set_protocol(fsm, PROTO_GADGET);
>> + break;
>> + case OTG_STATE_A_HOST:
>
> otg_drv_vbus(fsm, 1);

OK.

>
>> + 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;
>> +}
>> +

cheers,
-roger

2015-07-17 10:47:28

by Roger Quadros

[permalink] [raw]
Subject: Re: [PATCH v3 10/11] usb: otg: Add dual-role device (DRD) support

On 17/07/15 12:02, Li Jun wrote:
> On Wed, Jul 08, 2015 at 01:19:36PM +0300, Roger Quadros wrote:
>
> [...]
>
>> struct otg_fsm *usb_otg_register(struct device *parent_dev,
>> - struct otg_fsm_ops *fsm_ops)
>> + struct otg_fsm_ops *fsm_ops,
>> + bool drd_only)
>> {
>> struct otg_data *otgd;
>> int ret = 0;
>> @@ -328,7 +482,15 @@ struct otg_fsm *usb_otg_register(struct device *parent_dev,
>> goto err_wq;
>> }
>>
>> - usb_otg_init_timers(otgd);
>> + otgd->drd_only = drd_only;
>> + /* For DRD mode we don't need OTG timers */
>> + if (!drd_only) {
>> + usb_otg_init_timers(otgd);
>> +
>> + /* FIXME: we ignore caller's timer ops */
>> + otgd->fsm_ops.add_timer = usb_otg_add_timer;
>> + otgd->fsm_ops.del_timer = usb_otg_del_timer;
>> + }
>>
>> /* save original start host/gadget ops */
>> otgd->start_host = fsm_ops->start_host;
>> @@ -338,9 +500,6 @@ struct otg_fsm *usb_otg_register(struct device *parent_dev,
>
> Your above override will be override back to be fsm_ops's by below copy:
>
> /* create copy of original ops */
> otgd->fsm_ops = *fsm_ops;
>
> So add/del_timer must be override after the copy.

Good catch :).

>
>> /* override ops */
>> otgd->fsm_ops.start_host = usb_otg_start_host;
>> otgd->fsm_ops.start_gadget = usb_otg_start_gadget;
>> - /* FIXME: we ignore caller's timer ops */
>> - otgd->fsm_ops.add_timer = usb_otg_add_timer;
>> - otgd->fsm_ops.del_timer = usb_otg_del_timer;
>> /* set otg ops */
>> otgd->fsm.ops = &otgd->fsm_ops;
>> otgd->fsm.otg = &otgd->otg;
>> @@ -443,8 +602,10 @@ static void usb_otg_stop_fsm(struct otg_fsm *fsm)
>> otgd->fsm_running = false;
>>
>> /* Stop state machine / timers */
>> - for (i = 0; i < ARRAY_SIZE(otgd->timers); i++)
>> - hrtimer_cancel(&otgd->timers[i].timer);
>> + if (!otgd->drd_only) {
>> + for (i = 0; i < ARRAY_SIZE(otgd->timers); i++)
>> + hrtimer_cancel(&otgd->timers[i].timer);
>> + }
>>
>> flush_workqueue(otgd->wq);
>> fsm->otg->state = OTG_STATE_UNDEFINED;
>> diff --git a/include/linux/usb/otg-fsm.h b/include/linux/usb/otg-fsm.h
>> index 22d8baa..ae9c30a 100644
>> --- a/include/linux/usb/otg-fsm.h
>> +++ b/include/linux/usb/otg-fsm.h
>> @@ -48,6 +48,11 @@ enum otg_fsm_timer {
>> /**
>> * struct otg_fsm - OTG state machine according to the OTG spec
>> *
>> + * DRD mode hardware Inputs
>> + *
>> + * @id: TRUE for B-device, FALSE for A-device.
>> + * @vbus: VBUS voltage in regulation.
>> + *
>> * OTG hardware Inputs
>> *
>> * Common inputs for A and B device
>> @@ -122,7 +127,8 @@ enum otg_fsm_timer {
>> */
>> struct otg_fsm {
>> /* Input */
>> - int id;
>> + int id; /* DRD + OTG */
>> + int vbus; /* DRD only */
>
> Existing b_sess_vld can be also used for drd only case, no need create
> a new flag.

b_sess_vld is a bit confusing to people not familiar with OTG.
My suggestion is to use dedicated 'vbus' flag for DRD case
for simplicity.

>
>> int adp_change;
>> int power_up;
>> int a_srp_det;

cheers,
-roger

2015-07-17 11:17:33

by Roger Quadros

[permalink] [raw]
Subject: Re: [PATCH v3 02/11] usb: otg-fsm: support multiple instances

On 13/07/15 04:39, Peter Chen wrote:
> On Fri, Jul 10, 2015 at 04:06:43PM +0800, Li Jun wrote:
>> On Wed, Jul 08, 2015 at 01:19:28PM +0300, Roger Quadros wrote:
>>> Move the state_changed variable into struct otg_fsm
>>> so that we can support multiple instances.
>>>
>> I am not sure if multiple instances may happen since OTG protocol requires
>> only one OTG port can be equipped on OTG device.
>>
>
> It is ok the software can support more than spec requires, the user can
> only use one and the spec may change in future :)
>
>> Li Jun
>>
>>> 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..42c6376 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,7 @@ 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;
>>> + fsm->state_changed = 1;
>>> if (fsm->otg->state == new_state)
>>> return 0;
>>
>> "fsm->state_changed = 1;" should be put here, I think.
>>
>
> Yes, seems it is the problem at current code.

Right. But I'll fix it as per Li's suggestion.

cheers,
-roger

>
>> Li Jun
>>
>>> VDBG("Set state: %s\n", usb_otg_state_string(new_state));
>>> @@ -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 ca508c2..243274f 100644
>>> --- a/include/linux/usb/otg-fsm.h
>>> +++ b/include/linux/usb/otg-fsm.h
>>> @@ -194,6 +194,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-07-17 12:06:36

by Roger Quadros

[permalink] [raw]
Subject: Re: [PATCH v3 07/11] usb: otg: add OTG core

Hi Li,

On 17/07/15 10:48, Li Jun wrote:
> Hi, Roger
>
> On Wed, Jul 08, 2015 at 01:19:33PM +0300, Roger Quadros wrote:
>> 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 | 768 +++++++++++++++++++++++++++++++++++++++++++
>> drivers/usb/common/usb-otg.h | 71 ++++
>> drivers/usb/core/Kconfig | 11 +-
>> include/linux/usb/otg.h | 91 ++++-
>> 8 files changed, 930 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 8133cef..b21278e 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -10640,12 +10640,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..0379034
>> --- /dev/null
>> +++ b/drivers/usb/common/usb-otg.c
>> @@ -0,0 +1,768 @@
>> +/**
>> + * 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/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"
>> +
>> +/* to link timer with callback data */
>> +struct otg_timer {
>> + struct hrtimer timer;
>> + ktime_t timeout;
>> + /* callback data */
>> + int *timeout_bit;
>> + struct otg_data *otgd;
>> +};
>> +
>> +struct otg_hcd {
>> + struct usb_hcd *hcd;
>> + unsigned int irqnum;
>> + unsigned long irqflags;
>> + struct otg_hcd_ops *ops;
>> +};
>> +
>> +struct otg_data {
>> + struct device *dev; /* HCD & GCD's parent device */
>> +
>> + struct otg_fsm fsm;
>> + /* HCD, GCD and usb_otg_state are present in otg_fsm->otg
>> + * HCD is bus_to_hcd(fsm->otg->host)
>> + * GCD is fsm->otg->gadget
>> + */
>> + struct otg_fsm_ops fsm_ops; /* private copy for override */
>> + struct usb_otg otg; /* allocator for fsm->otg */
>> +
>> + struct otg_hcd primary_hcd;
>> + struct otg_hcd shared_hcd;
>> +
>> + struct otg_gadget_ops *gadget_ops; /* interface to gadget f/w */
>> +
>> + /* saved hooks to OTG device */
>> + int (*start_host)(struct otg_fsm *fsm, int on);
>> + int (*start_gadget)(struct otg_fsm *fsm, int on);
>> +
>> + struct list_head list;
>> +
>> + struct work_struct work; /* OTG FSM work */
>> + struct workqueue_struct *wq;
>> +
>> + struct otg_timer timers[NUM_OTG_FSM_TIMERS];
>> +
>> + bool fsm_running;
>> + /* use otg->fsm.lock for serializing access */
>> +};
>> +
>> +/* OTG device list */
>> +LIST_HEAD(otg_list);
>> +static DEFINE_MUTEX(otg_list_mutex);
>> +
>> +static int usb_otg_hcd_is_primary_hcd(struct usb_hcd *hcd)
>> +{
>> + if (!hcd->primary_hcd)
>> + return 1;
>> + return hcd == hcd->primary_hcd;
>> +}
>> +
>> +/**
>> + * check if device is in our OTG list and return
>> + * otg_data, else NULL.
>> + *
>> + * otg_list_mutex must be held.
>> + */
>> +static struct otg_data *usb_otg_device_get_otgd(struct device *parent_dev)
>> +{
>> + struct otg_data *otgd;
>> +
>> + list_for_each_entry(otgd, &otg_list, list) {
>> + if (otgd->dev == parent_dev)
>> + return otgd;
>> + }
>> +
>> + return NULL;
>> +}
>> +
>> +/**
>> + * 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 otg_data *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 otg_data *otgd)
>> +{
>> + struct otg_fsm *fsm = &otgd->fsm;
>> +
>> + 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);
>> +
>> + otg_timer_init(A_WAIT_ENUM, otgd, set_tmout, TB_SRP_FAIL, NULL);
>
> 2 timers are missing: B_DATA_PLS, B_SSEND_SRP.

Those 2 are not used by usb-otg-fsm.c. We can add it when usb-otg-fsm.c is updated.

>
>> +}
>> +
>> +/**
>> + * OTG FSM ops function to add timer
>> + */
>> +static void usb_otg_add_timer(struct otg_fsm *fsm, enum otg_fsm_timer id)
>> +{
>> + struct otg_data *otgd = container_of(fsm, struct otg_data, 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 otg_data *otgd = container_of(fsm, struct otg_data, fsm);
>> + struct hrtimer *timer = &otgd->timers[id].timer;
>> +
>> + hrtimer_cancel(timer);
>> +}
>> +
>> +/**
>> + * OTG FSM ops function to start/stop host
>> + */
>> +static int usb_otg_start_host(struct otg_fsm *fsm, int on)
>> +{
>> + struct otg_data *otgd = container_of(fsm, struct otg_data, 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) {
>> + /* OTG device operations */
>> + if (otgd->start_host)
>> + otgd->start_host(fsm, 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);
>> +
>> + /* OTG device operations */
>> + if (otgd->start_host)
>> + otgd->start_host(fsm, on);
>> + }
>> +
>> + return 0;
>> +}
>
> I do not see much benefit by this override function, usb_add/remove_hcd
> can be simply included by controller's start_host function, also there
> maybe some additional operations after usb_add_hcd, but this override
> function limit usb_add_hcd() is the last step.

I had tried host start/stop way before but Alan's suggestion was to use
bind/unbind the host controller completely as that is much simpler

[1] http://article.gmane.org/gmane.linux.usb.general/123842

>
> Maybe your intention is to make usb_add_hcd is the only operation required
> to start host, so ideally controller driver need not define its start_host
> routine for this otg ops, I am not sure if this can work for different otg

Yes that was the intention.

> platforms. If the shared code is only usb_add/remove_hcd(), maybe leave this
> ops defined by controller driver can make core code simple and give flexibility
> to controller drivers.

We don't completely override start/stop_host(). The flexibility is still there.
We call controllers start_host(1) before starting the controller and controllers
start_host(0) after stopping the controller.
So the the controller can still do what they want in otg_fsm_ops.start_host/gadget().

The OTG core only takes care of actually starting/stopping the host controller.

If we don't do that then the code in usb_otg_start_host() has to be pasted
in every OTG controller driver. This is code duplication.

>
>> +
>> +/**
>> + * OTG FSM ops function to start/stop gadget
>> + */
>> +static int usb_otg_start_gadget(struct otg_fsm *fsm, int on)
>> +{
>> + struct otg_data *otgd = container_of(fsm, struct otg_data, 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) {
>> + /* OTG device operations */
>> + if (otgd->start_gadget)
>> + otgd->start_gadget(fsm, on);
>> +
>> + otgd->gadget_ops->start(fsm->otg->gadget);
>> + } else {
>> + otgd->gadget_ops->stop(fsm->otg->gadget);
>> +
>> + /* OTG device operations */
>> + if (otgd->start_gadget)
>> + otgd->start_gadget(fsm, on);
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +/**
>> + * OTG FSM work function
>> + */
>> +static void usb_otg_work(struct work_struct *work)
>> +{
>> + struct otg_data *otgd = container_of(work, struct otg_data, work);
>> +
>> + otg_statemachine(&otgd->fsm);
>
> Need consider runtime pm, or you want to rely on controller driver take
> care of it?

For simplicity let's say that controller driver takes care of it.

>
>> +}
>> +
>> +/**
>> + * usb_otg_register() - Register the OTG device to OTG core
>> + * @parent_device: parent device of Host & Gadget controllers.
>> + * @otg_fsm_ops: otg state machine ops.
>> + *
>> + * Register parent device that contains both HCD and GCD into
>> + * the USB OTG core. HCD and GCD will be prevented from starting
>> + * till both are available for use.
>> + *
>> + * Return: struct otg_fsm * if success, NULL if error.
>> + */
>> +struct otg_fsm *usb_otg_register(struct device *parent_dev,
>> + struct otg_fsm_ops *fsm_ops)
>> +{
>> + struct otg_data *otgd;
>> + int ret = 0;
>> +
>> + if (!parent_dev || !fsm_ops)
>> + return ERR_PTR(-EINVAL);
>> +
>> + /* already in list? */
>> + mutex_lock(&otg_list_mutex);
>> + if (usb_otg_device_get_otgd(parent_dev)) {
>> + dev_err(parent_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 = parent_dev;
>> + INIT_WORK(&otgd->work, usb_otg_work);
>> + otgd->wq = create_singlethread_workqueue("usb_otg");
>> + if (!otgd->wq) {
>> + dev_err(parent_dev, "otg: %s: can't create workqueue\n",
>> + __func__);
>> + ret = -ENODEV;
>> + goto err_wq;
>> + }
>> +
>> + usb_otg_init_timers(otgd);
>> +
>> + /* save original start host/gadget ops */
>> + otgd->start_host = fsm_ops->start_host;
>> + otgd->start_gadget = fsm_ops->start_gadget;
>> + /* create copy of original ops */
>> + otgd->fsm_ops = *fsm_ops;
>> + /* override ops */
>> + otgd->fsm_ops.start_host = usb_otg_start_host;
>> + otgd->fsm_ops.start_gadget = usb_otg_start_gadget;
>> + /* FIXME: we ignore caller's timer ops */
>> + otgd->fsm_ops.add_timer = usb_otg_add_timer;
>> + otgd->fsm_ops.del_timer = usb_otg_del_timer;
>> + /* set otg ops */
>> + otgd->fsm.ops = &otgd->fsm_ops;
>> + otgd->fsm.otg = &otgd->otg;
>> +
>> + mutex_init(&otgd->fsm.lock);
>> +
>> + list_add_tail(&otgd->list, &otg_list);
>> + mutex_unlock(&otg_list_mutex);
>> + 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
>> + * @parent_device: parent device of Host & Gadget controllers.
>> + *
>> + * Unregister parent OTG deviced from USB OTG core.
>> + * Prevents unregistering till both Host and Gadget controllers
>> + * have unregistered from the OTG core.
>> + *
>> + * Return: 0 on success, error value otherwise.
>> + */
>> +int usb_otg_unregister(struct device *parent_dev)
>> +{
>> + struct otg_data *otgd;
>> +
>> + mutex_lock(&otg_list_mutex);
>> + otgd = usb_otg_device_get_otgd(parent_dev);
>> + if (!otgd) {
>> + dev_err(parent_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(parent_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 otg_data *otgd = container_of(fsm, struct otg_data, 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;
>> + }
>> +
>
> This cannot work, fsm->otg->host is set in usb_otg_register_hcd(), which is
> called by usb_add_hcd(), but usb_add_hcd() should be called only if otg fsm
> already started to some A-device state, deadlock.

I've changed usb_add_hcd() behaviour. Now usb_otg_add_hcd() does the real work of adding
the hcd. usb_add_hcd() prevents the add if it is an otg hcd and just registers
with OTG core.

>
>> + 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 otg_data *otgd = container_of(fsm, struct otg_data, 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 otg_data *otgd = container_of(fsm, struct otg_data, 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 otg_data *otgd;
>> +
>> + mutex_lock(&otg_list_mutex);
>> + otgd = usb_otg_device_get_otgd(hcd_gcd_device->parent);
>> + if (!otgd) {
>> + dev_err(hcd_gcd_device, "otg: %s: invalid host/gadget device\n",
>> + __func__);
>> + mutex_unlock(&otg_list_mutex);
>> + return -ENODEV;
>> + }
>> +
>> + mutex_unlock(&otg_list_mutex);
>> + 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 otg_data *otgd;
>> + struct device *otg_dev = hcd->self.controller->parent;
>> +
>> + mutex_lock(&otg_list_mutex);
>> + otgd = usb_otg_device_get_otgd(otg_dev);
>> + if (!otgd) {
>> + dev_dbg(otg_dev, "otg: %s: device not registered to otg core\n",
>> + __func__);
>> + mutex_unlock(&otg_list_mutex);
>> + return -EINVAL;
>> + }
>> +
>> + mutex_unlock(&otg_list_mutex);
>> + /* 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);
>
> usb_otg_register_hcd() is called before usb_otg_add_hcd(), start fsm on
> this point can make sense since hcd has not been added?

for OTG/DRD HCD case:
- usb_add_hcd() does not really ADD (or START) the HCD. It just registers with OTG core.
- FSM takes care of ADDing (or STARTing) the HCD when it wants using the
usb_otg_add_hcd() call.
- FSM does not need HCD to be already added. It just needs it to be registered.
It takes care of strting it when it wants to.

>
>> + } 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 otg_data *otgd;
>> + struct usb_bus *bus = hcd_to_bus(hcd);
>> + struct device *otg_dev = bus->controller->parent;
>> +
>> + mutex_lock(&otg_list_mutex);
>> + otgd = usb_otg_device_get_otgd(otg_dev);
>> + if (!otgd) {
>> + dev_err(otg_dev, "otg: %s: device not registered to otg core\n",
>> + __func__);
>> + mutex_unlock(&otg_list_mutex);
>> + return -EINVAL;
>> + }
>> +
>> + mutex_unlock(&otg_list_mutex);
>> +
>> + 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(bus->controller));
>> + } else if (hcd == otgd->shared_hcd.hcd) {
>> + otgd->shared_hcd.hcd = NULL;
>> + dev_info(otg_dev, "otg: shared host %s unregistered\n",
>> + dev_name(bus->controller));
>> + } else {
>> + dev_err(otg_dev, "otg: host %s wasn't registered with otg\n",
>> + dev_name(bus->controller));
>> + 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 otg_data *otgd;
>> + struct device *otg_dev = gadget->dev.parent;
>> +
>> + mutex_lock(&otg_list_mutex);
>> + otgd = usb_otg_device_get_otgd(otg_dev);
>> + if (!otgd) {
>> + dev_err(otg_dev, "otg: %s: device not registered to otg core\n",
>> + __func__);
>> + mutex_unlock(&otg_list_mutex);
>> + return -EINVAL;
>> + }
>> +
>> + mutex_unlock(&otg_list_mutex);
>> +
>> + 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 otg_data *otgd;
>> + struct device *otg_dev = gadget->dev.parent;
>> +
>> + mutex_lock(&otg_list_mutex);
>> + otgd = usb_otg_device_get_otgd(otg_dev);
>> + if (!otgd) {
>> + dev_err(otg_dev, "otg: %s: device not registered to otg core\n",
>> + __func__);
>> + mutex_unlock(&otg_list_mutex);
>> + return -EINVAL;
>> + }
>> +
>> + mutex_unlock(&otg_list_mutex);
>> +
>> + 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 otg_data *otgd = container_of(fsm, struct otg_data, 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)
>> +
>
> All otg timer timeout value should be in some *range* defined by otg spec,
> not some specific value, I don't think one specific value can meet all otg
> platforms, so we need find a way to make those value can be configured by
> controller drivers.

OK. How about introducing 'struct usb_otg_config' which must be passed
to usb_otg_register().

/* otg controller configuration */
struct usb_otg_config {
/* OTG caps */
struct usb_otg_caps otg_caps;

/* OTG Timer timeouts in ms. If 0, sane default will be used */
int ta_wait_vrise;
...
};

>
>> +#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 52661c5..ce6f8d8 100644
>> --- a/include/linux/usb/otg.h
>> +++ b/include/linux/usb/otg.h
>> @@ -10,6 +10,11 @@
>> #define __LINUX_USB_OTG_H
>>
>> #include <linux/phy/phy.h>
>> +#include <linux/device.h>
>> +#include <linux/usb.h>
>> +#include <linux/usb/hcd.h>
>> +#include <linux/usb/gadget.h>
>> +#include <linux/usb/otg-fsm.h>
>> #include <linux/usb/phy.h>
>>
>> struct usb_otg {
>> @@ -23,6 +28,7 @@ struct usb_otg {
>>
>> enum usb_otg_state state;
>>
>> +/*------------- deprecated interface -----------------------------*/
>> /* bind/unbind the host controller */
>> int (*set_host)(struct usb_otg *otg, struct usb_bus *host);
>>
>> @@ -38,11 +44,85 @@ struct usb_otg {
>>
>> /* start or continue HNP role switch */
>> int (*start_hnp)(struct usb_otg *otg);
>> -
>> +/*---------------------------------------------------------------*/
>> };
>>
>> 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 *parent_dev,
>> + struct otg_fsm_ops *fsm_ops);
>> +int usb_otg_unregister(struct device *parent_dev);
>> +int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
>> + unsigned long irqflags, struct otg_hcd_ops *ops);
>> +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);
>> +
>> +#else /* CONFIG_USB_OTG */
>> +
>> +static inline struct otg_fsm *usb_otg_register(struct device *parent_dev,
>> + struct otg_fsm_ops *fsm_ops)
>> +{
>> + return ERR_PTR(-ENOSYS);
>> +}
>> +
>> +static inline int usb_otg_unregister(struct device *parent_dev)
>> +{
>> + return -ENOSYS;
>> +}
>> +
>> +static inline int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
>> + unsigned long irqflags,
>> + struct otg_hcd_ops *ops)
>> +{
>> + return -ENOSYS;
>> +}
>> +
>> +static inline int usb_otg_unregister_hcd(struct usb_hcd *hcd)
>> +{
>> + return -ENOSYS;
>> +}
>> +
>> +static inline int usb_otg_register_gadget(struct usb_gadget *gadget,
>> + struct otg_gadget_ops *ops)
>> +{
>> + return -ENOSYS;
>> +}
>> +
>> +static inline int usb_otg_unregister_gadget(struct usb_gadget *gadget)
>> +{
>> + return -ENOSYS;
>> +}
>> +
>> +static inline void usb_otg_sync_inputs(struct otg_fsm *fsm)
>> +{
>> +}
>> +
>> +static inline int usb_otg_kick_fsm(struct device *hcd_gcd_device)
>> +{
>> + return -ENOSYS;
>> +}
>> +
>> +static inline struct device *usb_otg_fsm_to_dev(struct otg_fsm *fsm)
>> +{
>> + return NULL;
>> +}
>> +
>> +#endif /* CONFIG_USB_OTG */
>> +
>> +/*------------- deprecated interface -----------------------------*/
>> /* Context: can sleep */
>> static inline int
>> otg_start_hnp(struct usb_otg *otg)
>> @@ -94,14 +174,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
>>
>
> I assume I will have more comments after try more.

Sure, no worries :)

cheers,
-roger

2015-07-20 02:32:43

by Peter Chen

[permalink] [raw]
Subject: Re: [PATCH v3 10/11] usb: otg: Add dual-role device (DRD) support

On Fri, Jul 17, 2015 at 01:47:12PM +0300, Roger Quadros wrote:
> >> + * DRD mode hardware Inputs
> >> + *
> >> + * @id: TRUE for B-device, FALSE for A-device.
> >> + * @vbus: VBUS voltage in regulation.
> >> + *
> >> * OTG hardware Inputs
> >> *
> >> * Common inputs for A and B device
> >> @@ -122,7 +127,8 @@ enum otg_fsm_timer {
> >> */
> >> struct otg_fsm {
> >> /* Input */
> >> - int id;
> >> + int id; /* DRD + OTG */
> >> + int vbus; /* DRD only */
> >
> > Existing b_sess_vld can be also used for drd only case, no need create
> > a new flag.
>
> b_sess_vld is a bit confusing to people not familiar with OTG.
> My suggestion is to use dedicated 'vbus' flag for DRD case
> for simplicity.
>

Since OTG DRD is the subset in OTG FSM (FSM, data structure, APIs, etc),
I agree with Jun to reuse existing variables, and we can add some comments
for b_sess_vld if needed.


> >
> >> int adp_change;
> >> int power_up;
> >> int a_srp_det;
>
> cheers,
> -roger

--

Best Regards,
Peter Chen

2015-07-20 02:54:34

by Peter Chen

[permalink] [raw]
Subject: Re: [PATCH v3 07/11] usb: otg: add OTG core

On Fri, Jul 17, 2015 at 03:06:18PM +0300, Roger Quadros wrote:
> >> +
> >> +/**
> >> + * OTG FSM ops function to start/stop host
> >> + */
> >> +static int usb_otg_start_host(struct otg_fsm *fsm, int on)
> >> +{
> >> + struct otg_data *otgd = container_of(fsm, struct otg_data, 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) {
> >> + /* OTG device operations */
> >> + if (otgd->start_host)
> >> + otgd->start_host(fsm, 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);
> >> +
> >> + /* OTG device operations */
> >> + if (otgd->start_host)
> >> + otgd->start_host(fsm, on);
> >> + }
> >> +
> >> + return 0;
> >> +}
> >
> > I do not see much benefit by this override function, usb_add/remove_hcd
> > can be simply included by controller's start_host function, also there
> > maybe some additional operations after usb_add_hcd, but this override
> > function limit usb_add_hcd() is the last step.
>
> I had tried host start/stop way before but Alan's suggestion was to use
> bind/unbind the host controller completely as that is much simpler
>
> [1] http://article.gmane.org/gmane.linux.usb.general/123842
>

Jun may want we have some ways to override otg_fsm->ops by platform
drivers, I think we all agree to call usb_add_hcd/usb_remove_hcd
to start/stop host roles, just some platforms may need more than
just call them.

--

Best Regards,
Peter Chen

2015-07-21 12:01:54

by Li Jun

[permalink] [raw]
Subject: Re: [PATCH v3 07/11] usb: otg: add OTG core

Hi,

[...]

> >> + otg_timer_init(A_WAIT_ENUM, otgd, set_tmout, TB_SRP_FAIL, NULL);
> >
> > 2 timers are missing: B_DATA_PLS, B_SSEND_SRP.
>
> Those 2 are not used by usb-otg-fsm.c. We can add it when usb-otg-fsm.c is updated.
>

ok.

> >
> >> +}

[...]

> >> +
> >> +/**
> >> + * OTG FSM ops function to start/stop host
> >> + */
> >> +static int usb_otg_start_host(struct otg_fsm *fsm, int on)
> >> +{
> >> + struct otg_data *otgd = container_of(fsm, struct otg_data, 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) {
> >> + /* OTG device operations */
> >> + if (otgd->start_host)
> >> + otgd->start_host(fsm, 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);
> >> +
> >> + /* OTG device operations */
> >> + if (otgd->start_host)
> >> + otgd->start_host(fsm, on);
> >> + }
> >> +
> >> + return 0;
> >> +}
> >
> > I do not see much benefit by this override function, usb_add/remove_hcd
> > can be simply included by controller's start_host function, also there
> > maybe some additional operations after usb_add_hcd, but this override
> > function limit usb_add_hcd() is the last step.
>
> I had tried host start/stop way before but Alan's suggestion was to use
> bind/unbind the host controller completely as that is much simpler
>
> [1] http://article.gmane.org/gmane.linux.usb.general/123842
>

I did not mean host start/stop in your first version, I agree using
usb_add/remove_hcd() for simple.

> >
> > Maybe your intention is to make usb_add_hcd is the only operation required
> > to start host, so ideally controller driver need not define its start_host
> > routine for this otg ops, I am not sure if this can work for different otg
>
> Yes that was the intention.
>
> > platforms. If the shared code is only usb_add/remove_hcd(), maybe leave this
> > ops defined by controller driver can make core code simple and give flexibility
> > to controller drivers.
>
> We don't completely override start/stop_host(). The flexibility is still there.
> We call controllers start_host(1) before starting the controller and controllers
> start_host(0) after stopping the controller.
> So the the controller can still do what they want in otg_fsm_ops.start_host/gadget().
>

But if controller driver wants to do something after usb_otg_add_hcd(),
it's impossible with your current usb_otg_start_host().

> The OTG core only takes care of actually starting/stopping the host controller.
>
> If we don't do that then the code in usb_otg_start_host() has to be pasted
> in every OTG controller driver. This is code duplication.
>

Actually the only duplication code may be a function call to original
usb_add/remove_hcd().

> >
> >> +
> >> +/**
> >> + * OTG FSM ops function to start/stop gadget
> >> + */
> >> +static int usb_otg_start_gadget(struct otg_fsm *fsm, int on)
> >> +{
> >> + struct otg_data *otgd = container_of(fsm, struct otg_data, 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) {
> >> + /* OTG device operations */
> >> + if (otgd->start_gadget)
> >> + otgd->start_gadget(fsm, on);
> >> +
> >> + otgd->gadget_ops->start(fsm->otg->gadget);
> >> + } else {
> >> + otgd->gadget_ops->stop(fsm->otg->gadget);
> >> +
> >> + /* OTG device operations */
> >> + if (otgd->start_gadget)
> >> + otgd->start_gadget(fsm, on);
> >> + }
> >> +
> >> + return 0;
> >> +}
> >> +
> >> +/**
> >> + * OTG FSM work function
> >> + */
> >> +static void usb_otg_work(struct work_struct *work)
> >> +{
> >> + struct otg_data *otgd = container_of(work, struct otg_data, work);
> >> +
> >> + otg_statemachine(&otgd->fsm);
> >
> > Need consider runtime pm, or you want to rely on controller driver take
> > care of it?
>
> For simplicity let's say that controller driver takes care of it.
>

Then controller driver need add runtime pm for every otg fsm ops.

Code like below can make it simple:
runtime_pm_get_sync(otgd->dev);
otg_statemachine(&otgd->fsm);
runtime_pm_get_put(otgd->dev);

There is another problem, otg work will only do one state transition, but
in some cases we may need successive state transitions.

> >
> >> +}
> >> +
> >> +/**
> >> + * usb_otg_register() - Register the OTG device to OTG core
> >> + * @parent_device: parent device of Host & Gadget controllers.
> >> + * @otg_fsm_ops: otg state machine ops.
> >> + *

[...]

> >> +/**
> >> + * start/kick the OTG FSM if we can
> >> + * fsm->lock must be held
> >> + */
> >> +static void usb_otg_start_fsm(struct otg_fsm *fsm)
> >> +{
> >> + struct otg_data *otgd = container_of(fsm, struct otg_data, 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;
> >> + }
> >> +
> >
> > This cannot work, fsm->otg->host is set in usb_otg_register_hcd(), which is
> > called by usb_add_hcd(), but usb_add_hcd() should be called only if otg fsm
> > already started to some A-device state, deadlock.
>
> I've changed usb_add_hcd() behaviour. Now usb_otg_add_hcd() does the real work of adding
> the hcd. usb_add_hcd() prevents the add if it is an otg hcd and just registers
> with OTG core.
>

So you expect the controller driver still call usb_add_hcd() before otg fsm
start, in which it only registers the created hcd with OTG core.

> >
> >> + 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);
> >> +}
> >> +

[...]

> >> +
> >> +/**
> >> + * 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 otg_data *otgd;
> >> + struct device *otg_dev = hcd->self.controller->parent;
> >> +

I see normally we directly use controller dev for hcd->self.controller,
usb_create_hcd(... struct device *dev, ...)
{
... ...
hcd->self.controller = dev;
... ...
}

For register gadget, it's okay since:
int usb_add_gadget_udc_release(struct device *parent, ...)
{
... ...
gadget->dev.parent = parent;
... ...
}

So we need parent dev for usb_otg_register(struct device *dev,...), and child dev
for usb_create_hcd(struct device *dev,...)? dwc3 is designed like this?

> >> + mutex_lock(&otg_list_mutex);
> >> + otgd = usb_otg_device_get_otgd(otg_dev);
> >> + if (!otgd) {
> >> + dev_dbg(otg_dev, "otg: %s: device not registered to otg core\n",
> >> + __func__);
> >> + mutex_unlock(&otg_list_mutex);
> >> + return -EINVAL;
> >> + }
> >> +
> >> + mutex_unlock(&otg_list_mutex);
> >> + /* 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);
> >
> > usb_otg_register_hcd() is called before usb_otg_add_hcd(), start fsm on
> > this point can make sense since hcd has not been added?
>
> for OTG/DRD HCD case:
> - usb_add_hcd() does not really ADD (or START) the HCD. It just registers with OTG core.
> - FSM takes care of ADDing (or STARTing) the HCD when it wants using the
> usb_otg_add_hcd() call.

Understood.

> - FSM does not need HCD to be already added. It just needs it to be registered.

My point is only registering hcd to OTG core cannot be a valid *input* to make
otg fsm state can be changed, so it's making no sense to call usb_otg_start_fsm(),
but it's no harm.

> It takes care of strting it when it wants to.
>

Any otg fsm state change(or start it to make its state change) need some otg fsm
input or variables change happen.

> >
> >> + } 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);

[...]

> >> +#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)
> >> +
> >
> > All otg timer timeout value should be in some *range* defined by otg spec,
> > not some specific value, I don't think one specific value can meet all otg
> > platforms, so we need find a way to make those value can be configured by
> > controller drivers.
>
> OK. How about introducing 'struct usb_otg_config' which must be passed
> to usb_otg_register().
>

I think it's okay.

> /* otg controller configuration */
> struct usb_otg_config {
> /* OTG caps */
> struct usb_otg_caps otg_caps;

You can use a pointer to avoid data copy.

>
> /* OTG Timer timeouts in ms. If 0, sane default will be used */
> int ta_wait_vrise;
> ...
> };
>

> >
> >> 2.1.4
> >>
> >
> > I assume I will have more comments after try more.
>
> Sure, no worries :)
>
> cheers,
> -roger

2015-07-27 09:32:05

by Roger Quadros

[permalink] [raw]
Subject: Re: [PATCH v3 10/11] usb: otg: Add dual-role device (DRD) support

On 20/07/15 04:23, Peter Chen wrote:
> On Fri, Jul 17, 2015 at 01:47:12PM +0300, Roger Quadros wrote:
>>>> + * DRD mode hardware Inputs
>>>> + *
>>>> + * @id: TRUE for B-device, FALSE for A-device.
>>>> + * @vbus: VBUS voltage in regulation.
>>>> + *
>>>> * OTG hardware Inputs
>>>> *
>>>> * Common inputs for A and B device
>>>> @@ -122,7 +127,8 @@ enum otg_fsm_timer {
>>>> */
>>>> struct otg_fsm {
>>>> /* Input */
>>>> - int id;
>>>> + int id; /* DRD + OTG */
>>>> + int vbus; /* DRD only */
>>>
>>> Existing b_sess_vld can be also used for drd only case, no need create
>>> a new flag.
>>
>> b_sess_vld is a bit confusing to people not familiar with OTG.
>> My suggestion is to use dedicated 'vbus' flag for DRD case
>> for simplicity.
>>
>
> Since OTG DRD is the subset in OTG FSM (FSM, data structure, APIs, etc),
> I agree with Jun to reuse existing variables, and we can add some comments
> for b_sess_vld if needed.

OK then. I'll get rid of vbus and use b_sess_vld.

cheers,
-roger

>
>
>>>
>>>> int adp_change;
>>>> int power_up;
>>>> int a_srp_det;
>>

2015-07-27 10:03:23

by Roger Quadros

[permalink] [raw]
Subject: Re: [PATCH v3 07/11] usb: otg: add OTG core

Hi,

On 21/07/15 13:52, Li Jun wrote:
> Hi,
>
> [...]
>
>>>> + otg_timer_init(A_WAIT_ENUM, otgd, set_tmout, TB_SRP_FAIL, NULL);
>>>
>>> 2 timers are missing: B_DATA_PLS, B_SSEND_SRP.
>>
>> Those 2 are not used by usb-otg-fsm.c. We can add it when usb-otg-fsm.c is updated.
>>
>
> ok.
>
>>>
>>>> +}
>
> [...]
>
>>>> +
>>>> +/**
>>>> + * OTG FSM ops function to start/stop host
>>>> + */
>>>> +static int usb_otg_start_host(struct otg_fsm *fsm, int on)
>>>> +{
>>>> + struct otg_data *otgd = container_of(fsm, struct otg_data, 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) {
>>>> + /* OTG device operations */
>>>> + if (otgd->start_host)
>>>> + otgd->start_host(fsm, 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);
>>>> +
>>>> + /* OTG device operations */
>>>> + if (otgd->start_host)
>>>> + otgd->start_host(fsm, on);
>>>> + }
>>>> +
>>>> + return 0;
>>>> +}
>>>
>>> I do not see much benefit by this override function, usb_add/remove_hcd
>>> can be simply included by controller's start_host function, also there
>>> maybe some additional operations after usb_add_hcd, but this override
>>> function limit usb_add_hcd() is the last step.
>>
>> I had tried host start/stop way before but Alan's suggestion was to use
>> bind/unbind the host controller completely as that is much simpler
>>
>> [1] http://article.gmane.org/gmane.linux.usb.general/123842
>>
>
> I did not mean host start/stop in your first version, I agree using
> usb_add/remove_hcd() for simple.
>
>>>
>>> Maybe your intention is to make usb_add_hcd is the only operation required
>>> to start host, so ideally controller driver need not define its start_host
>>> routine for this otg ops, I am not sure if this can work for different otg
>>
>> Yes that was the intention.
>>
>>> platforms. If the shared code is only usb_add/remove_hcd(), maybe leave this
>>> ops defined by controller driver can make core code simple and give flexibility
>>> to controller drivers.
>>
>> We don't completely override start/stop_host(). The flexibility is still there.
>> We call controllers start_host(1) before starting the controller and controllers
>> start_host(0) after stopping the controller.
>> So the the controller can still do what they want in otg_fsm_ops.start_host/gadget().
>>
>
> But if controller driver wants to do something after usb_otg_add_hcd(),
> it's impossible with your current usb_otg_start_host().

Agree with that point. I can't forsee if any driver will need to do that but
we don't want to limit it so i'll consider your point of letting the controller drivers
do whatever they want in start/stop ops.

I can move the existing starts/stop to a library function so they can re-use it if they
don't want anything special.

>
>> The OTG core only takes care of actually starting/stopping the host controller.
>>
>> If we don't do that then the code in usb_otg_start_host() has to be pasted
>> in every OTG controller driver. This is code duplication.
>>
>
> Actually the only duplication code may be a function call to original
> usb_add/remove_hcd().

For USB hosts having primary and shared controllers it is not that simple
but they can use the library function in that case.

>
>>>
>>>> +
>>>> +/**
>>>> + * OTG FSM ops function to start/stop gadget
>>>> + */
>>>> +static int usb_otg_start_gadget(struct otg_fsm *fsm, int on)
>>>> +{
>>>> + struct otg_data *otgd = container_of(fsm, struct otg_data, 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) {
>>>> + /* OTG device operations */
>>>> + if (otgd->start_gadget)
>>>> + otgd->start_gadget(fsm, on);
>>>> +
>>>> + otgd->gadget_ops->start(fsm->otg->gadget);
>>>> + } else {
>>>> + otgd->gadget_ops->stop(fsm->otg->gadget);
>>>> +
>>>> + /* OTG device operations */
>>>> + if (otgd->start_gadget)
>>>> + otgd->start_gadget(fsm, on);
>>>> + }
>>>> +
>>>> + return 0;
>>>> +}
>>>> +
>>>> +/**
>>>> + * OTG FSM work function
>>>> + */
>>>> +static void usb_otg_work(struct work_struct *work)
>>>> +{
>>>> + struct otg_data *otgd = container_of(work, struct otg_data, work);
>>>> +
>>>> + otg_statemachine(&otgd->fsm);
>>>
>>> Need consider runtime pm, or you want to rely on controller driver take
>>> care of it?
>>
>> For simplicity let's say that controller driver takes care of it.
>>
>
> Then controller driver need add runtime pm for every otg fsm ops.
>
> Code like below can make it simple:
> runtime_pm_get_sync(otgd->dev);
> otg_statemachine(&otgd->fsm);
> runtime_pm_get_put(otgd->dev);

That can be done.
>
> There is another problem, otg work will only do one state transition, but
> in some cases we may need successive state transitions.

How can we fix this?

>
>>>
>>>> +}
>>>> +
>>>> +/**
>>>> + * usb_otg_register() - Register the OTG device to OTG core
>>>> + * @parent_device: parent device of Host & Gadget controllers.
>>>> + * @otg_fsm_ops: otg state machine ops.
>>>> + *
>
> [...]
>
>>>> +/**
>>>> + * start/kick the OTG FSM if we can
>>>> + * fsm->lock must be held
>>>> + */
>>>> +static void usb_otg_start_fsm(struct otg_fsm *fsm)
>>>> +{
>>>> + struct otg_data *otgd = container_of(fsm, struct otg_data, 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;
>>>> + }
>>>> +
>>>
>>> This cannot work, fsm->otg->host is set in usb_otg_register_hcd(), which is
>>> called by usb_add_hcd(), but usb_add_hcd() should be called only if otg fsm
>>> already started to some A-device state, deadlock.
>>
>> I've changed usb_add_hcd() behaviour. Now usb_otg_add_hcd() does the real work of adding
>> the hcd. usb_add_hcd() prevents the add if it is an otg hcd and just registers
>> with OTG core.
>>
>
> So you expect the controller driver still call usb_add_hcd() before otg fsm
> start, in which it only registers the created hcd with OTG core.

Yes.
>
>>>
>>>> + 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);
>>>> +}
>>>> +
>
> [...]
>
>>>> +
>>>> +/**
>>>> + * 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 otg_data *otgd;
>>>> + struct device *otg_dev = hcd->self.controller->parent;
>>>> +
>
> I see normally we directly use controller dev for hcd->self.controller,
> usb_create_hcd(... struct device *dev, ...)
> {
> ... ...
> hcd->self.controller = dev;
> ... ...
> }
>
> For register gadget, it's okay since:
> int usb_add_gadget_udc_release(struct device *parent, ...)
> {
> ... ...
> gadget->dev.parent = parent;
> ... ...
> }
>
> So we need parent dev for usb_otg_register(struct device *dev,...), and child dev
> for usb_create_hcd(struct device *dev,...)? dwc3 is designed like this?

Yes. But as we discussed in the cover letter this parent child relationship
doesn't need to be a requirement for device tree case.

>
>>>> + mutex_lock(&otg_list_mutex);
>>>> + otgd = usb_otg_device_get_otgd(otg_dev);
>>>> + if (!otgd) {
>>>> + dev_dbg(otg_dev, "otg: %s: device not registered to otg core\n",
>>>> + __func__);
>>>> + mutex_unlock(&otg_list_mutex);
>>>> + return -EINVAL;
>>>> + }
>>>> +
>>>> + mutex_unlock(&otg_list_mutex);
>>>> + /* 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);
>>>
>>> usb_otg_register_hcd() is called before usb_otg_add_hcd(), start fsm on
>>> this point can make sense since hcd has not been added?
>>
>> for OTG/DRD HCD case:
>> - usb_add_hcd() does not really ADD (or START) the HCD. It just registers with OTG core.
>> - FSM takes care of ADDing (or STARTing) the HCD when it wants using the
>> usb_otg_add_hcd() call.
>
> Understood.
>
>> - FSM does not need HCD to be already added. It just needs it to be registered.
>
> My point is only registering hcd to OTG core cannot be a valid *input* to make
> otg fsm state can be changed, so it's making no sense to call usb_otg_start_fsm(),
> but it's no harm.
>
>> It takes care of strting it when it wants to.
>>
>
> Any otg fsm state change(or start it to make its state change) need some otg fsm
> input or variables change happen.

Yes but the OTG FSM can't start till all the resources it needs are available.
i.e. both host and gadget controllers are ready.

>
>>>
>>>> + } 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);
>
> [...]
>
>>>> +#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)
>>>> +
>>>
>>> All otg timer timeout value should be in some *range* defined by otg spec,
>>> not some specific value, I don't think one specific value can meet all otg
>>> platforms, so we need find a way to make those value can be configured by
>>> controller drivers.
>>
>> OK. How about introducing 'struct usb_otg_config' which must be passed
>> to usb_otg_register().
>>
>
> I think it's okay.
>
>> /* otg controller configuration */
>> struct usb_otg_config {
>> /* OTG caps */
>> struct usb_otg_caps otg_caps;
>
> You can use a pointer to avoid data copy.

OK.

>
>>
>> /* OTG Timer timeouts in ms. If 0, sane default will be used */
>> int ta_wait_vrise;
>> ...
>> };
>>

cheers,
-roger

2015-08-14 09:54:19

by Peter Chen

[permalink] [raw]
Subject: Re: [PATCH v3 07/11] usb: otg: add OTG core

On Fri, Aug 14, 2015 at 12:42:38PM +0300, Roger Quadros wrote:
> Hi Peter,
>
> On 13/07/15 13:20, Roger Quadros wrote:
> > On 13/07/15 05:14, Peter Chen wrote:
> >> On Wed, Jul 08, 2015 at 01:19:33PM +0300, Roger Quadros wrote:
> >>> 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 | 768 +++++++++++++++++++++++++++++++++++++++++++
> >>> drivers/usb/common/usb-otg.h | 71 ++++
> >>> drivers/usb/core/Kconfig | 11 +-
> >>> include/linux/usb/otg.h | 91 ++++-
> >>> 8 files changed, 930 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 8133cef..b21278e 100644
> >>> --- a/MAINTAINERS
> >>> +++ b/MAINTAINERS
> >>> @@ -10640,12 +10640,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
> >>>
> >>
> >> USB_OTG can depends on USB || UB_GADGET?
> >
> > I didn't understand. The above is for USB_COMMON.
> >
> >>
> >>> 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/
> >>>
> >>
> >> The comment like above.
> >
> > What should it look like?
> >
>
> Can you please clarify what you meant at the above two
> comments? Thanks.
>

Forget them, I had thought the USB_OTG could be module.

--

Best Regards,
Peter Chen

2015-08-14 09:48:43

by Roger Quadros

[permalink] [raw]
Subject: Re: [PATCH v3 07/11] usb: otg: add OTG core

Hi Peter,

On 13/07/15 13:20, Roger Quadros wrote:
> On 13/07/15 05:14, Peter Chen wrote:
>> On Wed, Jul 08, 2015 at 01:19:33PM +0300, Roger Quadros wrote:
>>> 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 | 768 +++++++++++++++++++++++++++++++++++++++++++
>>> drivers/usb/common/usb-otg.h | 71 ++++
>>> drivers/usb/core/Kconfig | 11 +-
>>> include/linux/usb/otg.h | 91 ++++-
>>> 8 files changed, 930 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 8133cef..b21278e 100644
>>> --- a/MAINTAINERS
>>> +++ b/MAINTAINERS
>>> @@ -10640,12 +10640,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
>>>
>>
>> USB_OTG can depends on USB || UB_GADGET?
>
> I didn't understand. The above is for USB_COMMON.
>
>>
>>> 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/
>>>
>>
>> The comment like above.
>
> What should it look like?
>

Can you please clarify what you meant at the above two
comments? Thanks.

cheers,
-roger