This is an attempt to centralize OTG/Dual-role functionality in the kernel.
As of now I've got Dual-role functionality working pretty reliably on
dra7-evm. xhci side of things for OTG/DRD use are fixed in
http://thread.gmane.org/gmane.linux.kernel/1923161
Changelog:
---------
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);
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);
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);
'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.
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.
Pending items:
- We probably need a otg class.
- sysfs interface for application OTG inputs and OTG status information
- resolve symbol dependency for module use.
--
cheers,
-roger
Roger Quadros (13):
usb: otg-fsm: Add documentation for struct otg_fsm
usb: otg-fsm: support multiple instances
usb: otg-fsm: Prevent build warning "VDBG" redefined
usb: gadget: add usb_gadget_start/stop()
usb: otg: add OTG core
usb: hcd: Add hcd add/remove functions for OTG use
usb: otg: Add dual-role device (DRD) support
usb: otg: hub: Notify OTG fsm when A device sets b_hnp_enable
usb: gadget: udc: adapt to OTG
udc-core: fix lock circular dependency on udc_lock
usb: add "dual-role" mode to dr_mode device tree helper
usb: dwc3: add dual-role support
ARM: dts: dra7x-evm: Enable dual-role for usb1
Documentation/devicetree/bindings/usb/generic.txt | 2 +-
arch/arm/boot/dts/dra7-evm.dts | 2 +-
arch/arm/boot/dts/dra72-evm.dts | 2 +-
drivers/usb/Makefile | 1 +
drivers/usb/common/Makefile | 1 +
drivers/usb/common/common.c | 1 +
drivers/usb/common/usb-otg-fsm.c | 19 +-
drivers/usb/common/usb-otg.c | 902 ++++++++++++++++++++++
drivers/usb/common/usb-otg.h | 71 ++
drivers/usb/core/Kconfig | 8 +
drivers/usb/core/hcd.c | 24 +-
drivers/usb/core/hub.c | 11 +-
drivers/usb/dwc3/core.c | 146 +++-
drivers/usb/dwc3/core.h | 6 +
drivers/usb/dwc3/platform_data.h | 1 +
drivers/usb/gadget/udc/udc-core.c | 119 ++-
include/linux/usb/gadget.h | 3 +
include/linux/usb/hcd.h | 3 +
include/linux/usb/otg-fsm.h | 104 ++-
include/linux/usb/otg.h | 1 +
include/linux/usb/usb-otg.h | 95 +++
21 files changed, 1476 insertions(+), 46 deletions(-)
create mode 100644 drivers/usb/common/usb-otg.c
create mode 100644 drivers/usb/common/usb-otg.h
create mode 100644 include/linux/usb/usb-otg.h
--
2.1.0
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 | 80 +++++++++++++++++++++++++++++++++++++++++----
1 file changed, 73 insertions(+), 7 deletions(-)
diff --git a/include/linux/usb/otg-fsm.h b/include/linux/usb/otg-fsm.h
index b6ba1bf..c5b74c5 100644
--- a/include/linux/usb/otg-fsm.h
+++ b/include/linux/usb/otg-fsm.h
@@ -57,37 +57,103 @@ 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
+ * FIXME: 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
+ * @a_sess_vld: ??
+ * @b_bus_suspend: ??
+ * @b_bus_resume: ??
+ *
+ * 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.
+ * FIXME: 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
+ * FIXME: 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;
@@ -95,7 +161,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.0
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 1736bbe..0caa37b 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));
@@ -255,7 +253,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) {
@@ -368,7 +366,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 c5b74c5..85a9150 100644
--- a/include/linux/usb/otg-fsm.h
+++ b/include/linux/usb/otg-fsm.h
@@ -183,6 +183,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.0
If usb/otg-fsm.h and usb/composite.h are included together
then it results in the build warning [1].
Prevent that by moving the VDBG defination into the
usb-otg-fsm.c file where it is used.
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/common/usb-otg-fsm.c | 9 +++++++++
include/linux/usb/otg-fsm.h | 15 ---------------
2 files changed, 9 insertions(+), 15 deletions(-)
diff --git a/drivers/usb/common/usb-otg-fsm.c b/drivers/usb/common/usb-otg-fsm.c
index 0caa37b..35f311a 100644
--- a/drivers/usb/common/usb-otg-fsm.c
+++ b/drivers/usb/common/usb-otg-fsm.c
@@ -30,6 +30,15 @@
#include <linux/usb/otg.h>
#include <linux/usb/otg-fsm.h>
+#undef VDBG
+
+#ifdef VERBOSE
+#define VDBG(fmt, args...) pr_debug("[%s] " fmt , \
+ __func__, ## args)
+#else
+#define VDBG(stuff...) do {} while (0)
+#endif
+
/* Change USB protocol when there is a protocol change */
static int otg_set_protocol(struct otg_fsm *fsm, int protocol)
{
diff --git a/include/linux/usb/otg-fsm.h b/include/linux/usb/otg-fsm.h
index 85a9150..73136aa 100644
--- a/include/linux/usb/otg-fsm.h
+++ b/include/linux/usb/otg-fsm.h
@@ -21,21 +21,6 @@
#include <linux/mutex.h>
#include <linux/errno.h>
-#undef VERBOSE
-
-#ifdef VERBOSE
-#define VDBG(fmt, args...) pr_debug("[%s] " fmt , \
- __func__, ## args)
-#else
-#define VDBG(stuff...) do {} while (0)
-#endif
-
-#ifdef VERBOSE
-#define MPC_LOC printk("Current Location [%s]:[%d]\n", __FILE__, __LINE__)
-#else
-#define MPC_LOC do {} while (0)
-#endif
-
#define PROTO_UNDEF (0)
#define PROTO_HOST (1)
#define PROTO_GADGET (2)
--
2.1.0
The OTG state machine needs a mechanism to start and
stop the gadget controller. Add usb_gadget_start()
and usb_gadget_stop().
Signed-off-by: Roger Quadros <[email protected]>
---
drivers/usb/gadget/udc/udc-core.c | 74 +++++++++++++++++++++++++++++++++++++++
include/linux/usb/gadget.h | 3 ++
2 files changed, 77 insertions(+)
diff --git a/drivers/usb/gadget/udc/udc-core.c b/drivers/usb/gadget/udc/udc-core.c
index 5a81cb0..3aa5dd5 100644
--- a/drivers/usb/gadget/udc/udc-core.c
+++ b/drivers/usb/gadget/udc/udc-core.c
@@ -187,6 +187,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);
}
@@ -204,10 +205,83 @@ 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).
+ */
+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_gadget_connect(udc->gadget);
+
+ mutex_unlock(&udc_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(usb_gadget_start);
+
+/**
+ * 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.
+ */
+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;
+}
+EXPORT_SYMBOL_GPL(usb_gadget_stop);
+
+/**
* usb_udc_release - release the usb_udc struct
* @dev: the dev member within usb_udc
*
diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h
index e2f00fd..7ada7e6 100644
--- a/include/linux/usb/gadget.h
+++ b/include/linux/usb/gadget.h
@@ -922,6 +922,9 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver);
*/
int usb_gadget_unregister_driver(struct usb_gadget_driver *driver);
+int usb_gadget_start(struct usb_gadget *gadget);
+int usb_gadget_stop(struct usb_gadget *gadget);
+
extern int usb_add_gadget_udc_release(struct device *parent,
struct usb_gadget *gadget, void (*release)(struct device *dev));
extern int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget);
--
2.1.0
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
TODO:
- sysfs interface to allow application inputs to OTG state machine
- otg class?
Signed-off-by: Roger Quadros <[email protected]>
---
drivers/usb/Makefile | 1 +
drivers/usb/common/Makefile | 1 +
drivers/usb/common/usb-otg.c | 743 +++++++++++++++++++++++++++++++++++++++++++
drivers/usb/common/usb-otg.h | 71 +++++
drivers/usb/core/Kconfig | 8 +
include/linux/usb/usb-otg.h | 94 ++++++
6 files changed, 918 insertions(+)
create mode 100644 drivers/usb/common/usb-otg.c
create mode 100644 drivers/usb/common/usb-otg.h
create mode 100644 include/linux/usb/usb-otg.h
diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
index 2f1e2aa..07f59a5 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_CORE) += common/
obj-$(CONFIG_USBIP_CORE) += usbip/
diff --git a/drivers/usb/common/Makefile b/drivers/usb/common/Makefile
index ca2f8bd..573fc75 100644
--- a/drivers/usb/common/Makefile
+++ b/drivers/usb/common/Makefile
@@ -7,3 +7,4 @@ 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_OTG_CORE) += usb-otg.o
diff --git a/drivers/usb/common/usb-otg.c b/drivers/usb/common/usb-otg.c
new file mode 100644
index 0000000..e848e08
--- /dev/null
+++ b/drivers/usb/common/usb-otg.c
@@ -0,0 +1,743 @@
+/**
+ * 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/usb/usb-otg.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_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;
+
+ /* 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);
+
+/**
+ * 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);
+
+ 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 */
+ usb_add_hcd(otgd->primary_hcd.hcd, otgd->primary_hcd.irqnum,
+ otgd->primary_hcd.irqflags);
+ if (otgd->shared_hcd.hcd) {
+ usb_add_hcd(otgd->shared_hcd.hcd,
+ otgd->shared_hcd.irqnum,
+ otgd->shared_hcd.irqflags);
+ }
+ } else {
+ /* stop host */
+ if (otgd->shared_hcd.hcd)
+ usb_remove_hcd(otgd->shared_hcd.hcd);
+ usb_remove_hcd(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);
+
+ usb_gadget_start(fsm->otg->gadget);
+ } else {
+ usb_gadget_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);
+
+ if (otg_statemachine(&otgd->fsm)) {
+ /* state changed. any action ? */
+ }
+}
+
+/**
+ * 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
+ *
+ * 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_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_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;
+ 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_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;
+ 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_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;
+ 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 cc0ced0..43a0d2d 100644
--- a/drivers/usb/core/Kconfig
+++ b/drivers/usb/core/Kconfig
@@ -84,3 +84,11 @@ config USB_OTG_FSM
Implements OTG Finite State Machine as specified in On-The-Go
and Embedded Host Supplement to the USB Revision 2.0 Specification.
+config USB_OTG_CORE
+ tristate "USB OTG core"
+ depends on USB
+ select USB_OTG_FSM
+ help
+ Standardize the way OTG is implemented on Linux. The OTG state
+ machine is instantiated here instead of being done in each controller
+ driver.
diff --git a/include/linux/usb/usb-otg.h b/include/linux/usb/usb-otg.h
new file mode 100644
index 0000000..8987cd1
--- /dev/null
+++ b/include/linux/usb/usb-otg.h
@@ -0,0 +1,94 @@
+/**
+ * include/linux/usb/usb-otg.h - 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.
+ */
+
+#ifndef __LINUX_USB_OTG_CORE_H
+#define __LINUX_USB_OTG_CORE_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>
+
+#if IS_ENABLED(CONFIG_USB_OTG_CORE)
+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);
+int usb_otg_unregister_hcd(struct usb_hcd *hcd);
+int usb_otg_register_gadget(struct usb_gadget *gadget);
+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);
+bool usb_otg_gadget_can_start(struct usb_gadget *gadget);
+struct device *usb_otg_fsm_to_dev(struct otg_fsm *fsm);
+
+#else /* CONFIG_USB_OTG_CORE */
+
+static inline struct otg_fsm *usb_otg_register(struct device *parent_dev,
+ struct otg_fsm_ops *fsm_ops)
+{
+ 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)
+{
+ return -ENOSYS;
+}
+
+int usb_otg_unregister_hcd(struct usb_hcd *hcd)
+{
+ return -ENOSYS;
+}
+
+static inline int usb_otg_register_gadget(struct usb_gadget *gadget)
+{
+ 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)
+{
+}
+
+int usb_otg_kick_fsm(struct device *hcd_gcd_device)
+{
+ return -ENOSYS;
+}
+
+bool usb_otg_gadget_can_start(struct usb_gadget *gadget)
+{
+ return true;
+}
+
+static inline struct device *usb_otg_fsm_to_dev(struct otg_fsm *fsm)
+{
+ return NULL;
+}
+#endif /* CONFIG_USB_OTG_CORE */
+
+#endif /* __LINUX_USB_OTG_CORE */
--
2.1.0
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_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 | 14 +++++++-------
drivers/usb/core/hcd.c | 24 ++++++++++++++++++++++--
include/linux/usb/hcd.h | 3 +++
3 files changed, 32 insertions(+), 9 deletions(-)
diff --git a/drivers/usb/common/usb-otg.c b/drivers/usb/common/usb-otg.c
index e848e08..860e2e7 100644
--- a/drivers/usb/common/usb-otg.c
+++ b/drivers/usb/common/usb-otg.c
@@ -198,18 +198,18 @@ static int usb_otg_start_host(struct otg_fsm *fsm, int on)
otgd->start_host(fsm, on);
/* start host */
- usb_add_hcd(otgd->primary_hcd.hcd, otgd->primary_hcd.irqnum,
- otgd->primary_hcd.irqflags);
+ _usb_add_hcd(otgd->primary_hcd.hcd, otgd->primary_hcd.irqnum,
+ otgd->primary_hcd.irqflags);
if (otgd->shared_hcd.hcd) {
- usb_add_hcd(otgd->shared_hcd.hcd,
- otgd->shared_hcd.irqnum,
- otgd->shared_hcd.irqflags);
+ _usb_add_hcd(otgd->shared_hcd.hcd,
+ otgd->shared_hcd.irqnum,
+ otgd->shared_hcd.irqflags);
}
} else {
/* stop host */
if (otgd->shared_hcd.hcd)
- usb_remove_hcd(otgd->shared_hcd.hcd);
- usb_remove_hcd(otgd->primary_hcd.hcd);
+ _usb_remove_hcd(otgd->shared_hcd.hcd);
+ _usb_remove_hcd(otgd->primary_hcd.hcd);
/* OTG device operations */
if (otgd->start_host)
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index 45a915c..9a9c0f7 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/usb-otg.h>
#include "usb.h"
@@ -2622,7 +2623,7 @@ 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,
+int _usb_add_hcd(struct usb_hcd *hcd,
unsigned int irqnum, unsigned long irqflags)
{
int retval;
@@ -2827,6 +2828,17 @@ err_phy:
}
return retval;
}
+EXPORT_SYMBOL_GPL(_usb_add_hcd);
+
+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))
+ return _usb_add_hcd(hcd, irqnum, irqflags);
+
+ return 0;
+}
EXPORT_SYMBOL_GPL(usb_add_hcd);
/**
@@ -2837,7 +2849,7 @@ EXPORT_SYMBOL_GPL(usb_add_hcd);
* 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)
+void _usb_remove_hcd(struct usb_hcd *hcd)
{
struct usb_device *rhdev = hcd->self.root_hub;
@@ -2911,6 +2923,14 @@ void usb_remove_hcd(struct usb_hcd *hcd)
usb_put_invalidate_rhdev(hcd);
}
+EXPORT_SYMBOL_GPL(_usb_remove_hcd);
+
+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_remove_hcd(hcd);
+}
EXPORT_SYMBOL_GPL(usb_remove_hcd);
void
diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h
index 68b1e83..7993ae7 100644
--- a/include/linux/usb/hcd.h
+++ b/include/linux/usb/hcd.h
@@ -433,7 +433,10 @@ extern void usb_put_hcd(struct usb_hcd *hcd);
extern int usb_hcd_is_primary_hcd(struct usb_hcd *hcd);
extern int usb_add_hcd(struct usb_hcd *hcd,
unsigned int irqnum, unsigned long irqflags);
+extern int _usb_add_hcd(struct usb_hcd *hcd,
+ unsigned int irqnum, unsigned long irqflags);
extern void usb_remove_hcd(struct usb_hcd *hcd);
+extern void _usb_remove_hcd(struct usb_hcd *hcd);
extern int usb_hcd_find_raw_port_number(struct usb_hcd *hcd, int port1);
struct platform_device;
--
2.1.0
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/usb-otg.h | 5 +-
3 files changed, 179 insertions(+), 13 deletions(-)
diff --git a/drivers/usb/common/usb-otg.c b/drivers/usb/common/usb-otg.c
index 860e2e7..6d6da86 100644
--- a/drivers/usb/common/usb-otg.c
+++ b/drivers/usb/common/usb-otg.c
@@ -44,6 +44,7 @@ struct otg_hcd {
struct otg_data {
struct device *dev; /* HCD & GCD's parent device */
+ bool drd_only; /* Dual-role only, no OTG features */
struct otg_fsm fsm;
/* HCD, GCD and usb_otg_state are present in otg_fsm->otg
* HCD is bus_to_hcd(fsm->otg->host)
@@ -251,22 +252,172 @@ static int usb_otg_start_gadget(struct otg_fsm *fsm, int on)
return 0;
}
+/* Change USB protocol when there is a protocol change */
+static int drd_set_protocol(struct otg_fsm *fsm, int protocol)
+{
+ struct otg_data *otgd = container_of(fsm, struct otg_data, fsm);
+ int ret = 0;
+
+ if (fsm->protocol != protocol) {
+ dev_dbg(otgd->dev, "drd: changing role fsm->protocol= %d; new protocol= %d\n",
+ fsm->protocol, protocol);
+ /* stop old protocol */
+ if (fsm->protocol == PROTO_HOST)
+ ret = otg_start_host(fsm, 0);
+ else if (fsm->protocol == PROTO_GADGET)
+ ret = otg_start_gadget(fsm, 0);
+ if (ret)
+ return ret;
+
+ /* start new protocol */
+ if (protocol == PROTO_HOST)
+ ret = otg_start_host(fsm, 1);
+ else if (protocol == PROTO_GADGET)
+ ret = otg_start_gadget(fsm, 1);
+ if (ret)
+ return ret;
+
+ fsm->protocol = protocol;
+ return 0;
+ }
+
+ return 0;
+}
+
+/* Called when entering a DRD state */
+static void drd_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state)
+{
+ struct otg_data *otgd = container_of(fsm, struct otg_data, fsm);
+
+ if (fsm->otg->state == new_state)
+ return;
+
+ fsm->state_changed = 1;
+ dev_dbg(otgd->dev, "drd: set state: %s\n",
+ usb_otg_state_string(new_state));
+ switch (new_state) {
+ case OTG_STATE_B_IDLE:
+ drd_set_protocol(fsm, PROTO_UNDEF);
+ break;
+ case OTG_STATE_B_PERIPHERAL:
+ drd_set_protocol(fsm, PROTO_GADGET);
+ break;
+ case OTG_STATE_A_HOST:
+ drd_set_protocol(fsm, PROTO_HOST);
+ break;
+ case OTG_STATE_UNDEFINED:
+ case OTG_STATE_B_SRP_INIT:
+ case OTG_STATE_B_WAIT_ACON:
+ case OTG_STATE_B_HOST:
+ case OTG_STATE_A_IDLE:
+ case OTG_STATE_A_WAIT_VRISE:
+ case OTG_STATE_A_WAIT_BCON:
+ case OTG_STATE_A_SUSPEND:
+ case OTG_STATE_A_PERIPHERAL:
+ case OTG_STATE_A_WAIT_VFALL:
+ case OTG_STATE_A_VBUS_ERR:
+ default:
+ dev_warn(otgd->dev, "%s: drd: invalid state: %s\n",
+ __func__, usb_otg_state_string(new_state));
+ break;
+ }
+
+ fsm->otg->state = new_state;
+}
+
/**
- * OTG FSM work function
+ * DRD state change judgement
+ *
+ * For DRD we're only interested in some of the OTG states
+ * i.e. OTG_STATE_B_IDLE: both peripheral and host are stopped
+ * OTG_STATE_B_PERIPHERAL: peripheral active
+ * OTG_STATE_A_HOST: host active
+ * we're only interested in the following inputs
+ * fsm->id, fsm->vbus
+ */
+static int drd_statemachine(struct otg_fsm *fsm)
+{
+ struct otg_data *otgd = container_of(fsm, struct otg_data, fsm);
+ enum usb_otg_state state;
+
+ mutex_lock(&fsm->lock);
+
+ state = fsm->otg->state;
+
+ switch (state) {
+ case OTG_STATE_UNDEFINED:
+ if (!fsm->id)
+ drd_set_state(fsm, OTG_STATE_A_HOST);
+ else if (fsm->id && fsm->vbus)
+ drd_set_state(fsm, OTG_STATE_B_PERIPHERAL);
+ else
+ drd_set_state(fsm, OTG_STATE_B_IDLE);
+ break;
+ case OTG_STATE_B_IDLE:
+ if (!fsm->id)
+ drd_set_state(fsm, OTG_STATE_A_HOST);
+ else if (fsm->vbus)
+ drd_set_state(fsm, OTG_STATE_B_PERIPHERAL);
+ break;
+ case OTG_STATE_B_PERIPHERAL:
+ if (!fsm->id)
+ drd_set_state(fsm, OTG_STATE_A_HOST);
+ else if (!fsm->vbus)
+ drd_set_state(fsm, OTG_STATE_B_IDLE);
+ break;
+ case OTG_STATE_A_HOST:
+ if (fsm->id && fsm->vbus)
+ drd_set_state(fsm, OTG_STATE_B_PERIPHERAL);
+ else if (fsm->id && !fsm->vbus)
+ drd_set_state(fsm, OTG_STATE_B_IDLE);
+ break;
+
+ /* invalid states for DRD */
+ case OTG_STATE_B_SRP_INIT:
+ case OTG_STATE_B_WAIT_ACON:
+ case OTG_STATE_B_HOST:
+ case OTG_STATE_A_IDLE:
+ case OTG_STATE_A_WAIT_VRISE:
+ case OTG_STATE_A_WAIT_BCON:
+ case OTG_STATE_A_SUSPEND:
+ case OTG_STATE_A_PERIPHERAL:
+ case OTG_STATE_A_WAIT_VFALL:
+ case OTG_STATE_A_VBUS_ERR:
+ dev_err(otgd->dev, "%s: drd: invalid usb-drd state: %s\n",
+ __func__, usb_otg_state_string(state));
+ drd_set_state(fsm, OTG_STATE_UNDEFINED);
+ break;
+ }
+
+ mutex_unlock(&fsm->lock);
+ dev_dbg(otgd->dev, "drd: quit statemachine, changed %d\n",
+ fsm->state_changed);
+
+ return fsm->state_changed;
+}
+
+/**
+ * OTG FSM/DRD work function
*/
static void usb_otg_work(struct work_struct *work)
{
struct otg_data *otgd = container_of(work, struct otg_data, work);
- if (otg_statemachine(&otgd->fsm)) {
- /* state changed. any action ? */
+ /* OTG state machine */
+ if (!otgd->drd_only) {
+ otg_statemachine(&otgd->fsm);
+ return;
}
+
+ /* DRD state machine */
+ drd_statemachine(&otgd->fsm);
}
/**
* usb_otg_register() - Register the OTG device to OTG core
* @parent_device: parent device of Host & Gadget controllers.
* @otg_fsm_ops: otg state machine ops.
+ * @drd_only: dual-role only. no OTG features.
*
* Register parent device that contains both HCD and GCD into
* the USB OTG core. HCD and GCD will be prevented from starting
@@ -275,7 +426,8 @@ static void usb_otg_work(struct work_struct *work)
* Return: struct otg_fsm * if success, NULL if error.
*/
struct otg_fsm *usb_otg_register(struct device *parent_dev,
- struct otg_fsm_ops *fsm_ops)
+ struct otg_fsm_ops *fsm_ops,
+ bool drd_only)
{
struct otg_data *otgd;
int ret = 0;
@@ -309,7 +461,15 @@ struct otg_fsm *usb_otg_register(struct device *parent_dev,
goto err_wq;
}
- usb_otg_init_timers(otgd);
+ otgd->drd_only = drd_only;
+ /* For DRD mode we don't need OTG timers */
+ if (!drd_only) {
+ usb_otg_init_timers(otgd);
+
+ /* FIXME: we ignore caller's timer ops */
+ otgd->fsm_ops.add_timer = usb_otg_add_timer;
+ otgd->fsm_ops.del_timer = usb_otg_del_timer;
+ }
/* save original start host/gadget ops */
otgd->start_host = fsm_ops->start_host;
@@ -319,9 +479,6 @@ struct otg_fsm *usb_otg_register(struct device *parent_dev,
/* override ops */
otgd->fsm_ops.start_host = usb_otg_start_host;
otgd->fsm_ops.start_gadget = usb_otg_start_gadget;
- /* FIXME: we ignore caller's timer ops */
- otgd->fsm_ops.add_timer = usb_otg_add_timer;
- otgd->fsm_ops.del_timer = usb_otg_del_timer;
/* set otg ops */
otgd->fsm.ops = &otgd->fsm_ops;
otgd->fsm.otg = &otgd->otg;
@@ -424,8 +581,10 @@ static void usb_otg_stop_fsm(struct otg_fsm *fsm)
otgd->fsm_running = false;
/* Stop state machine / timers */
- for (i = 0; i < ARRAY_SIZE(otgd->timers); i++)
- hrtimer_cancel(&otgd->timers[i].timer);
+ if (!otgd->drd_only) {
+ for (i = 0; i < ARRAY_SIZE(otgd->timers); i++)
+ hrtimer_cancel(&otgd->timers[i].timer);
+ }
flush_workqueue(otgd->wq);
fsm->otg->state = OTG_STATE_UNDEFINED;
diff --git a/include/linux/usb/otg-fsm.h b/include/linux/usb/otg-fsm.h
index 73136aa..44666aa 100644
--- a/include/linux/usb/otg-fsm.h
+++ b/include/linux/usb/otg-fsm.h
@@ -45,6 +45,11 @@ enum otg_fsm_timer {
/**
* struct otg_fsm - OTG state machine according to the OTG spec
*
+ * DRD mode hardware Inputs
+ *
+ * @id: TRUE for B-device, FALSE for A-device.
+ * @vbus: VBUS voltage in regulation.
+ *
* OTG hardware Inputs
*
* Common inputs for A and B device
@@ -110,7 +115,8 @@ enum otg_fsm_timer {
*/
struct otg_fsm {
/* Input */
- int id;
+ int id; /* DRD + OTG */
+ int vbus; /* DRD only */
int adp_change;
int power_up;
int a_srp_det;
diff --git a/include/linux/usb/usb-otg.h b/include/linux/usb/usb-otg.h
index 8987cd1..cc06a94 100644
--- a/include/linux/usb/usb-otg.h
+++ b/include/linux/usb/usb-otg.h
@@ -25,7 +25,7 @@
#if IS_ENABLED(CONFIG_USB_OTG_CORE)
struct otg_fsm *usb_otg_register(struct device *parent_dev,
- struct otg_fsm_ops *fsm_ops);
+ struct otg_fsm_ops *fsm_ops, bool drd_only);
int usb_otg_unregister(struct device *parent_dev);
int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
unsigned long irqflags);
@@ -40,7 +40,8 @@ struct device *usb_otg_fsm_to_dev(struct otg_fsm *fsm);
#else /* CONFIG_USB_OTG_CORE */
static inline struct otg_fsm *usb_otg_register(struct device *parent_dev,
- struct otg_fsm_ops *fsm_ops)
+ struct otg_fsm_ops *fsm_ops,
+ bool drd_only)
{
return ERR_PTR(-ENOSYS);
}
--
2.1.0
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 | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index d7c3d5a..ab0d498 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -22,6 +22,7 @@
#include <linux/usb/hcd.h>
#include <linux/usb/otg.h>
#include <linux/usb/quirks.h>
+#include <linux/usb/usb-otg.h>
#include <linux/workqueue.h>
#include <linux/mutex.h>
#include <linux/random.h>
@@ -2270,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);
}
}
}
@@ -4244,8 +4248,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.0
Register with OTG core when gadget function driver
is available and unregister when function driver is unbound.
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 | 34 ++++++++++++++++++++++++++--------
1 file changed, 26 insertions(+), 8 deletions(-)
diff --git a/drivers/usb/gadget/udc/udc-core.c b/drivers/usb/gadget/udc/udc-core.c
index 3aa5dd5..ad162e5 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/usb-otg.h>
/**
* struct usb_udc - describes one usb device controller
@@ -35,6 +36,7 @@
* @dev - the child device to the actual controller
* @gadget - the gadget. For use by the class code
* @list - for use by the udc class driver
+ * @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.
@@ -44,6 +46,7 @@ struct usb_udc {
struct usb_gadget *gadget;
struct device dev;
struct list_head list;
+ bool is_otg;
};
static struct class *udc_class;
@@ -403,10 +406,14 @@ 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 (usb_otg_unregister_gadget(udc->gadget)) {
+ 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;
@@ -466,12 +473,17 @@ 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 */
+ udc->is_otg = !usb_otg_register_gadget(udc->gadget);
+ if (!udc->is_otg) {
+ ret = usb_gadget_udc_start(udc);
+ if (ret) {
+ driver->unbind(udc->gadget);
+ goto err1;
+ }
+ usb_gadget_connect(udc->gadget);
}
- usb_gadget_connect(udc->gadget);
kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE);
return 0;
@@ -583,6 +595,12 @@ 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);
--
2.1.0
we need to unlock the usb_lock mutex before calling
usb_otg_register_gadget() else it will cause a
circular locking dependency.
Signed-off-by: Roger Quadros <[email protected]>
---
drivers/usb/gadget/udc/udc-core.c | 13 +++++++++++--
1 file changed, 11 insertions(+), 2 deletions(-)
diff --git a/drivers/usb/gadget/udc/udc-core.c b/drivers/usb/gadget/udc/udc-core.c
index ad162e5..6246d09 100644
--- a/drivers/usb/gadget/udc/udc-core.c
+++ b/drivers/usb/gadget/udc/udc-core.c
@@ -399,6 +399,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",
@@ -407,7 +408,11 @@ static void usb_gadget_remove_driver(struct usb_udc *udc)
kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE);
/* If OTG, the otg core ensures UDC is stopped on unregister */
- if (usb_otg_unregister_gadget(udc->gadget)) {
+ 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);
@@ -445,11 +450,12 @@ found:
dev_vdbg(gadget->dev.parent, "unregistering gadget\n");
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);
@@ -459,6 +465,7 @@ EXPORT_SYMBOL_GPL(usb_del_gadget_udc);
/* ------------------------------------------------------------------------- */
+/* udc_lock must be held */
static int udc_bind_to_driver(struct usb_udc *udc, struct usb_gadget_driver *driver)
{
int ret;
@@ -475,7 +482,9 @@ static int udc_bind_to_driver(struct usb_udc *udc, struct usb_gadget_driver *dri
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);
+ mutex_lock(&udc_lock);
if (!udc->is_otg) {
ret = usb_gadget_udc_start(udc);
if (ret) {
--
2.1.0
We need to differentiat between "otg" and "dual-role" operation.
dual-role means capability to operate as host or device depending
on the ID pin status but no support for any of the OTG features
like SRP, HNP and on the fly role-swap.
The only way to change the role for a dual-role device is to
toggle the ID pin by changing the cable.
Signed-off-by: Roger Quadros <[email protected]>
---
Documentation/devicetree/bindings/usb/generic.txt | 2 +-
drivers/usb/common/common.c | 1 +
include/linux/usb/otg.h | 1 +
3 files changed, 3 insertions(+), 1 deletion(-)
diff --git a/Documentation/devicetree/bindings/usb/generic.txt b/Documentation/devicetree/bindings/usb/generic.txt
index 477d5bb..64438f9 100644
--- a/Documentation/devicetree/bindings/usb/generic.txt
+++ b/Documentation/devicetree/bindings/usb/generic.txt
@@ -8,7 +8,7 @@ Optional properties:
HW capability.
- dr_mode: tells Dual-Role USB controllers that we want to work on a
particular mode. Valid arguments are "host",
- "peripheral" and "otg". In case this attribute isn't
+ "peripheral", "otg", "dual-role". In case this attribute isn't
passed via DT, USB DRD controllers should default to
OTG.
diff --git a/drivers/usb/common/common.c b/drivers/usb/common/common.c
index b530fd4..faf51fe 100644
--- a/drivers/usb/common/common.c
+++ b/drivers/usb/common/common.c
@@ -87,6 +87,7 @@ static const char *const usb_dr_modes[] = {
[USB_DR_MODE_HOST] = "host",
[USB_DR_MODE_PERIPHERAL] = "peripheral",
[USB_DR_MODE_OTG] = "otg",
+ [USB_DR_MODE_DRD] = "dual-role",
};
/**
diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h
index 52661c5..d6057fb 100644
--- a/include/linux/usb/otg.h
+++ b/include/linux/usb/otg.h
@@ -102,6 +102,7 @@ enum usb_dr_mode {
USB_DR_MODE_HOST,
USB_DR_MODE_PERIPHERAL,
USB_DR_MODE_OTG,
+ USB_DR_MODE_DRD,
};
#endif /* __LINUX_USB_OTG_H */
--
2.1.0
Register with the USB OTG core. Since we don't support
OTG yet we just work as a dual-role device even
if device tree says "otg".
Use extcon framework to get VBUS/ID cable events and
kick the OTG state machine.
Signed-off-by: Roger Quadros <[email protected]>
---
drivers/usb/dwc3/core.c | 146 ++++++++++++++++++++++++++++++++++++++-
drivers/usb/dwc3/core.h | 6 ++
drivers/usb/dwc3/platform_data.h | 1 +
3 files changed, 151 insertions(+), 2 deletions(-)
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 9f0e209..3e04b96 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -39,6 +39,7 @@
#include <linux/usb/gadget.h>
#include <linux/usb/of.h>
#include <linux/usb/otg.h>
+#include <linux/usb/usb-otg.h>
#include "platform_data.h"
#include "core.h"
@@ -504,7 +505,8 @@ static int dwc3_core_init(struct dwc3 *dwc)
* SOF/ITP Mode Used
*/
if ((dwc->dr_mode == USB_DR_MODE_HOST ||
- dwc->dr_mode == USB_DR_MODE_OTG) &&
+ dwc->dr_mode == USB_DR_MODE_OTG ||
+ dwc->dr_mode == USB_DR_MODE_DRD) &&
(dwc->revision >= DWC3_REVISION_210A &&
dwc->revision <= DWC3_REVISION_250A))
reg |= DWC3_GCTL_DSBLCLKGTNG | DWC3_GCTL_SOFITPSYNC;
@@ -656,6 +658,119 @@ static int dwc3_core_get_phy(struct dwc3 *dwc)
return 0;
}
+/* --------------------- Dual-Role management ------------------------------- */
+
+static void dwc3_drd_fsm_sync(struct dwc3 *dwc)
+{
+ int id, vbus;
+
+ /* get ID */
+ id = extcon_get_cable_state(dwc->edev, "USB-HOST");
+ /* Host means ID == 0 */
+ id = !id;
+
+ /* get VBUS */
+ vbus = extcon_get_cable_state(dwc->edev, "USB");
+ dev_dbg(dwc->dev, "id %d vbus %d\n", id, vbus);
+
+ dwc->fsm->id = id;
+ dwc->fsm->vbus = vbus;
+ usb_otg_sync_inputs(dwc->fsm);
+}
+
+static int dwc3_drd_start_host(struct otg_fsm *fsm, int on)
+{
+ struct device *dev = usb_otg_fsm_to_dev(fsm);
+ struct dwc3 *dwc = dev_get_drvdata(dev);
+
+ dev_dbg(dwc->dev, "%s: %d\n", __func__, on);
+ if (on)
+ dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_HOST);
+
+ return 0;
+}
+
+static int dwc3_drd_start_gadget(struct otg_fsm *fsm, int on)
+{
+ struct device *dev = usb_otg_fsm_to_dev(fsm);
+ struct dwc3 *dwc = dev_get_drvdata(dev);
+
+ dev_dbg(dwc->dev, "%s: %d\n", __func__, on);
+ if (on) {
+ dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE);
+ dwc3_event_buffers_setup(dwc);
+ }
+
+ return 0;
+}
+
+static struct otg_fsm_ops dwc3_drd_ops = {
+ .start_host = dwc3_drd_start_host,
+ .start_gadget = dwc3_drd_start_gadget,
+};
+
+static int dwc3_drd_notifier(struct notifier_block *nb,
+ unsigned long event, void *ptr)
+{
+ struct dwc3 *dwc = container_of(nb, struct dwc3, otg_nb);
+
+ dwc3_drd_fsm_sync(dwc);
+
+ return NOTIFY_DONE;
+}
+
+static int dwc3_drd_init(struct dwc3 *dwc)
+{
+ int ret, id, vbus;
+
+ if (!dwc->edev) {
+ dev_err(dwc->dev, "No extcon device found for OTG mode\n");
+ return -ENODEV;
+ }
+
+ dwc->otg_nb.notifier_call = dwc3_drd_notifier;
+ ret = extcon_register_notifier(dwc->edev, &dwc->otg_nb);
+ if (ret < 0) {
+ dev_err(dwc->dev, "Couldn't register USB cable notifier\n");
+ return -ENODEV;
+ }
+
+ /* sanity check id & vbus states */
+ id = extcon_get_cable_state(dwc->edev, "USB-HOST");
+ vbus = extcon_get_cable_state(dwc->edev, "USB");
+ if (id < 0 || vbus < 0) {
+ dev_err(dwc->dev, "Invalid USB cable state. id %d, vbus %d\n",
+ id, vbus);
+ ret = -ENODEV;
+ goto fail;
+ }
+
+ /* register parent as OTG device with USB core */
+ dwc->fsm = usb_otg_register(dwc->dev, &dwc3_drd_ops, true);
+ if (IS_ERR(dwc->fsm)) {
+ dev_err(dwc->dev, "Failed to register with OTG core\n");
+ ret = -ENODEV;
+ goto fail;
+ }
+
+ dwc3_drd_fsm_sync(dwc);
+
+ return 0;
+
+fail:
+ extcon_unregister_notifier(dwc->edev, &dwc->otg_nb);
+
+ return ret;
+}
+
+static void dwc3_drd_exit(struct dwc3 *dwc)
+{
+ usb_otg_unregister(dwc->dev);
+ extcon_unregister_notifier(dwc->edev, &dwc->otg_nb);
+}
+
+/* -------------------------------------------------------------------------- */
+
static int dwc3_core_init_mode(struct dwc3 *dwc)
{
struct device *dev = dwc->dev;
@@ -679,7 +794,15 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
}
break;
case USB_DR_MODE_OTG:
- dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_OTG);
+ dev_info(dev, "otg not implemented, we'll work as dual-role\n");
+ /* fall through */
+ case USB_DR_MODE_DRD:
+ ret = dwc3_drd_init(dwc);
+ if (ret) {
+ dev_err(dev, "failed to initialize drd\n");
+ return ret;
+ }
+
ret = dwc3_host_init(dwc);
if (ret) {
dev_err(dev, "failed to initialize host\n");
@@ -710,8 +833,10 @@ static void dwc3_core_exit_mode(struct dwc3 *dwc)
dwc3_host_exit(dwc);
break;
case USB_DR_MODE_OTG:
+ case USB_DR_MODE_DRD:
dwc3_host_exit(dwc);
dwc3_gadget_exit(dwc);
+ dwc3_drd_exit(dwc);
break;
default:
/* do nothing */
@@ -799,6 +924,16 @@ static int dwc3_probe(struct platform_device *pdev)
hird_threshold = 12;
if (node) {
+ if (of_property_read_bool(node, "extcon"))
+ dwc->edev = extcon_get_edev_by_phandle(dev, 0);
+ else if (of_property_read_bool(dev->parent->of_node, "extcon"))
+ dwc->edev = extcon_get_edev_by_phandle(dev->parent, 0);
+
+ if (IS_ERR(dwc->edev)) {
+ dev_vdbg(dev, "couldn't get extcon device\n");
+ return -EPROBE_DEFER;
+ }
+
dwc->maximum_speed = of_usb_get_maximum_speed(node);
dwc->has_lpm_erratum = of_property_read_bool(node,
"snps,has-lpm-erratum");
@@ -839,6 +974,13 @@ static int dwc3_probe(struct platform_device *pdev)
of_property_read_u8(node, "snps,tx_de_emphasis",
&tx_de_emphasis);
} else if (pdata) {
+ if (pdata->extcon) {
+ dwc->edev = extcon_get_extcon_dev(pdata->extcon);
+ if (!dwc->edev) {
+ dev_vdbg(dev, "couldn't get extcon device\n");
+ return -EPROBE_DEFER;
+ }
+ }
dwc->maximum_speed = pdata->maximum_speed;
dwc->has_lpm_erratum = pdata->has_lpm_erratum;
if (pdata->lpm_nyet_threshold)
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index d201910..2ece013 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -30,8 +30,10 @@
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
#include <linux/usb/otg.h>
+#include <linux/usb/otg-fsm.h>
#include <linux/phy/phy.h>
+#include <linux/extcon.h>
#define DWC3_MSG_MAX 500
@@ -738,6 +740,10 @@ struct dwc3 {
struct phy *usb2_generic_phy;
struct phy *usb3_generic_phy;
+ struct extcon_dev *edev; /* USB cable events ID & VBUS */
+ struct notifier_block otg_nb; /* notifier for USB cable events */
+ struct otg_fsm *fsm;
+
void __iomem *regs;
size_t regs_size;
diff --git a/drivers/usb/dwc3/platform_data.h b/drivers/usb/dwc3/platform_data.h
index a3a3b6d5..9552601 100644
--- a/drivers/usb/dwc3/platform_data.h
+++ b/drivers/usb/dwc3/platform_data.h
@@ -24,6 +24,7 @@ struct dwc3_platform_data {
enum usb_device_speed maximum_speed;
enum usb_dr_mode dr_mode;
bool tx_fifo_resize;
+ const char *extcon; /* extcon name for USB cable events ID/VBUS */
unsigned is_utmi_l1_suspend:1;
u8 hird_threshold;
--
2.1.0
Now that we have dual-role support working at USB core,
enable dual-role support for usb1 controller.
Signed-off-by: Roger Quadros <[email protected]>
---
arch/arm/boot/dts/dra7-evm.dts | 2 +-
arch/arm/boot/dts/dra72-evm.dts | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/arch/arm/boot/dts/dra7-evm.dts b/arch/arm/boot/dts/dra7-evm.dts
index 746cddb..2f495c9 100644
--- a/arch/arm/boot/dts/dra7-evm.dts
+++ b/arch/arm/boot/dts/dra7-evm.dts
@@ -552,7 +552,7 @@
};
&usb1 {
- dr_mode = "peripheral";
+ dr_mode = "dual-role";
pinctrl-names = "default";
pinctrl-0 = <&usb1_pins>;
};
diff --git a/arch/arm/boot/dts/dra72-evm.dts b/arch/arm/boot/dts/dra72-evm.dts
index e0264d0..93cdf89 100644
--- a/arch/arm/boot/dts/dra72-evm.dts
+++ b/arch/arm/boot/dts/dra72-evm.dts
@@ -381,7 +381,7 @@
};
&usb1 {
- dr_mode = "peripheral";
+ dr_mode = "dual-role";
pinctrl-names = "default";
pinctrl-0 = <&usb1_pins>;
};
--
2.1.0
(This will go into a minor detail. That's probably not what you want
when posting an RFC. But this patch got caught by an email filter I use
and a future, non-RFC, version will get caught too. So I decided to
bother you with this now.)
On Tue, 2015-04-14 at 13:41 +0300, Roger Quadros wrote:
> --- a/drivers/usb/common/Makefile
> +++ b/drivers/usb/common/Makefile
> +obj-$(CONFIG_USB_OTG_CORE) += usb-otg.o
This creates a module usb-otg.ko if CONFIG_USB_OTG_CORE is 'm', built
from just usb-otg.c.
> --- /dev/null
> +++ b/drivers/usb/common/usb-otg.c
> + * 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.
> +EXPORT_SYMBOL_GPL(usb_otg_register);
> +EXPORT_SYMBOL_GPL(usb_otg_unregister);
> +EXPORT_SYMBOL_GPL(usb_otg_sync_inputs);
> +EXPORT_SYMBOL_GPL(usb_otg_kick_fsm);
> +EXPORT_SYMBOL_GPL(usb_otg_register_hcd);
> +EXPORT_SYMBOL_GPL(usb_otg_unregister_hcd);
> +EXPORT_SYMBOL_GPL(usb_otg_register_gadget);
> +EXPORT_SYMBOL_GPL(usb_otg_unregister_gadget);
> +EXPORT_SYMBOL_GPL(usb_otg_fsm_to_dev);
This code adds no MODULE_LICENSE() macro in this patch and usb-otg.ko
will carry no license field. So I think the loading of that module
(triggered by loading another module that uses one of the exported
symbols, I suppose) taints the kernel. Am I correct?
Paul Bolle
On 15/04/15 12:29, Paul Bolle wrote:
> (This will go into a minor detail. That's probably not what you want
> when posting an RFC. But this patch got caught by an email filter I use
> and a future, non-RFC, version will get caught too. So I decided to
> bother you with this now.)
>
> On Tue, 2015-04-14 at 13:41 +0300, Roger Quadros wrote:
>> --- a/drivers/usb/common/Makefile
>> +++ b/drivers/usb/common/Makefile
>
>> +obj-$(CONFIG_USB_OTG_CORE) += usb-otg.o
>
> This creates a module usb-otg.ko if CONFIG_USB_OTG_CORE is 'm', built
> from just usb-otg.c.
>
>> --- /dev/null
>> +++ b/drivers/usb/common/usb-otg.c
>
>> + * 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.
>
>> +EXPORT_SYMBOL_GPL(usb_otg_register);
>
>> +EXPORT_SYMBOL_GPL(usb_otg_unregister);
>
>> +EXPORT_SYMBOL_GPL(usb_otg_sync_inputs);
>
>> +EXPORT_SYMBOL_GPL(usb_otg_kick_fsm);
>
>> +EXPORT_SYMBOL_GPL(usb_otg_register_hcd);
>
>> +EXPORT_SYMBOL_GPL(usb_otg_unregister_hcd);
>
>> +EXPORT_SYMBOL_GPL(usb_otg_register_gadget);
>
>> +EXPORT_SYMBOL_GPL(usb_otg_unregister_gadget);
>
>> +EXPORT_SYMBOL_GPL(usb_otg_fsm_to_dev);
>
> This code adds no MODULE_LICENSE() macro in this patch and usb-otg.ko
> will carry no license field. So I think the loading of that module
> (triggered by loading another module that uses one of the exported
> symbols, I suppose) taints the kernel. Am I correct?
Right. I will fix this. Thanks.
cheers,
-roger
On Tue, Apr 14, 2015 at 01:41:48PM +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 | 80 +++++++++++++++++++++++++++++++++++++++++----
> 1 file changed, 73 insertions(+), 7 deletions(-)
>
> diff --git a/include/linux/usb/otg-fsm.h b/include/linux/usb/otg-fsm.h
> index b6ba1bf..c5b74c5 100644
> --- a/include/linux/usb/otg-fsm.h
> +++ b/include/linux/usb/otg-fsm.h
> @@ -57,37 +57,103 @@ 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
What's the intention you leave two spaces at above line?
> + * @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
> + * FIXME: must be set by host/hub driver
Yes, according to pid/vid pair.
> + *
> + * 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
> + * @a_sess_vld: ??
See OTG spec 1.3, and obsolete now.
> + * @b_bus_suspend: ??
> + * @b_bus_resume: ??
They are used at current otg fsm, just for B-host.
> + *
> + * 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.
> + * FIXME: OTG fsm uses otg->host->b_hnp_enable instead
a_set_b_hnp_en is for otg 2.0 and it should instead of
otg->host->b_hnp_enable at otg fsm.
> + * 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
> + * FIXME: OTG fsm uses otg->gadget->b_hnp_enable instead
Yes
> + * 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;
> @@ -95,7 +161,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.0
>
--
Best Regards,
Peter Chen
On Tue, Apr 14, 2015 at 01:41:49PM +0300, Roger Quadros wrote:
> Move the state_changed variable into struct otg_fsm
> so that we can support multiple instances.
OTG device has only one port. See 3.1.1.
If you go to pass OTG Certification, the lab requires that there is only
one port at your board.
>
> 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 1736bbe..0caa37b 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));
> @@ -255,7 +253,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) {
> @@ -368,7 +366,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 c5b74c5..85a9150 100644
> --- a/include/linux/usb/otg-fsm.h
> +++ b/include/linux/usb/otg-fsm.h
> @@ -183,6 +183,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.0
>
--
Best Regards,
Peter Chen
On Tue, Apr 14, 2015 at 01:41:50PM +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 moving the VDBG defination into the
> usb-otg-fsm.c file where it is used.
>
> 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/common/usb-otg-fsm.c | 9 +++++++++
> include/linux/usb/otg-fsm.h | 15 ---------------
> 2 files changed, 9 insertions(+), 15 deletions(-)
>
> diff --git a/drivers/usb/common/usb-otg-fsm.c b/drivers/usb/common/usb-otg-fsm.c
> index 0caa37b..35f311a 100644
> --- a/drivers/usb/common/usb-otg-fsm.c
> +++ b/drivers/usb/common/usb-otg-fsm.c
> @@ -30,6 +30,15 @@
> #include <linux/usb/otg.h>
> #include <linux/usb/otg-fsm.h>
>
> +#undef VDBG
> +
> +#ifdef VERBOSE
> +#define VDBG(fmt, args...) pr_debug("[%s] " fmt , \
> + __func__, ## args)
> +#else
> +#define VDBG(stuff...) do {} while (0)
> +#endif
> +
It is obsolete too, we may use dev_vdbg instead of it.
> /* Change USB protocol when there is a protocol change */
> static int otg_set_protocol(struct otg_fsm *fsm, int protocol)
> {
> diff --git a/include/linux/usb/otg-fsm.h b/include/linux/usb/otg-fsm.h
> index 85a9150..73136aa 100644
> --- a/include/linux/usb/otg-fsm.h
> +++ b/include/linux/usb/otg-fsm.h
> @@ -21,21 +21,6 @@
> #include <linux/mutex.h>
> #include <linux/errno.h>
>
> -#undef VERBOSE
> -
> -#ifdef VERBOSE
> -#define VDBG(fmt, args...) pr_debug("[%s] " fmt , \
> - __func__, ## args)
> -#else
> -#define VDBG(stuff...) do {} while (0)
> -#endif
> -
> -#ifdef VERBOSE
> -#define MPC_LOC printk("Current Location [%s]:[%d]\n", __FILE__, __LINE__)
> -#else
> -#define MPC_LOC do {} while (0)
> -#endif
> -
> #define PROTO_UNDEF (0)
> #define PROTO_HOST (1)
> #define PROTO_GADGET (2)
> --
> 2.1.0
>
--
Best Regards,
Peter Chen
On Tue, Apr 14, 2015 at 01:41:51PM +0300, Roger Quadros wrote:
> The OTG state machine needs a mechanism to start and
> stop the gadget controller. Add usb_gadget_start()
> and usb_gadget_stop().
>
> Signed-off-by: Roger Quadros <[email protected]>
> ---
> drivers/usb/gadget/udc/udc-core.c | 74 +++++++++++++++++++++++++++++++++++++++
> include/linux/usb/gadget.h | 3 ++
> 2 files changed, 77 insertions(+)
>
> diff --git a/drivers/usb/gadget/udc/udc-core.c b/drivers/usb/gadget/udc/udc-core.c
> index 5a81cb0..3aa5dd5 100644
> --- a/drivers/usb/gadget/udc/udc-core.c
> +++ b/drivers/usb/gadget/udc/udc-core.c
> @@ -187,6 +187,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);
> }
>
> @@ -204,10 +205,83 @@ 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).
> + */
> +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_gadget_connect(udc->gadget);
OTG FSM manages its pullup/pulldown itself through its fsm->ops->loc_conn
> +
> + mutex_unlock(&udc_lock);
> +
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(usb_gadget_start);
> +
> +/**
> + * 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.
> + */
> +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;
> +}
> +EXPORT_SYMBOL_GPL(usb_gadget_stop);
> +
> +/**
> * usb_udc_release - release the usb_udc struct
> * @dev: the dev member within usb_udc
> *
> diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h
> index e2f00fd..7ada7e6 100644
> --- a/include/linux/usb/gadget.h
> +++ b/include/linux/usb/gadget.h
> @@ -922,6 +922,9 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver);
> */
> int usb_gadget_unregister_driver(struct usb_gadget_driver *driver);
>
> +int usb_gadget_start(struct usb_gadget *gadget);
> +int usb_gadget_stop(struct usb_gadget *gadget);
> +
> extern int usb_add_gadget_udc_release(struct device *parent,
> struct usb_gadget *gadget, void (*release)(struct device *dev));
> extern int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget);
> --
> 2.1.0
>
--
Best Regards,
Peter Chen
On 16/04/15 14:36, Peter Chen wrote:
> On Tue, Apr 14, 2015 at 01:41:49PM +0300, Roger Quadros wrote:
>> Move the state_changed variable into struct otg_fsm
>> so that we can support multiple instances.
>
> OTG device has only one port. See 3.1.1.
> If you go to pass OTG Certification, the lab requires that there is only
> one port at your board.
>
We're not breaking OTG compliance by this ;).
Moreover this structure is not limited to OTG but for DRD (dual-role) use as well.
We can have multiple DRD ports per board.
cheers,
-roger
>>
>> 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 1736bbe..0caa37b 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));
>> @@ -255,7 +253,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) {
>> @@ -368,7 +366,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 c5b74c5..85a9150 100644
>> --- a/include/linux/usb/otg-fsm.h
>> +++ b/include/linux/usb/otg-fsm.h
>> @@ -183,6 +183,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.0
>>
>
On 16/04/15 14:41, Peter Chen wrote:
> On Tue, Apr 14, 2015 at 01:41:50PM +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 moving the VDBG defination into the
>> usb-otg-fsm.c file where it is used.
>>
>> 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/common/usb-otg-fsm.c | 9 +++++++++
>> include/linux/usb/otg-fsm.h | 15 ---------------
>> 2 files changed, 9 insertions(+), 15 deletions(-)
>>
>> diff --git a/drivers/usb/common/usb-otg-fsm.c b/drivers/usb/common/usb-otg-fsm.c
>> index 0caa37b..35f311a 100644
>> --- a/drivers/usb/common/usb-otg-fsm.c
>> +++ b/drivers/usb/common/usb-otg-fsm.c
>> @@ -30,6 +30,15 @@
>> #include <linux/usb/otg.h>
>> #include <linux/usb/otg-fsm.h>
>>
>> +#undef VDBG
>> +
>> +#ifdef VERBOSE
>> +#define VDBG(fmt, args...) pr_debug("[%s] " fmt , \
>> + __func__, ## args)
>> +#else
>> +#define VDBG(stuff...) do {} while (0)
>> +#endif
>> +
>
> It is obsolete too, we may use dev_vdbg instead of it.
ok, we need to add 'struct device *' to 'struct otg_fsm' then.
cheers,
-roger
>
>> /* Change USB protocol when there is a protocol change */
>> static int otg_set_protocol(struct otg_fsm *fsm, int protocol)
>> {
>> diff --git a/include/linux/usb/otg-fsm.h b/include/linux/usb/otg-fsm.h
>> index 85a9150..73136aa 100644
>> --- a/include/linux/usb/otg-fsm.h
>> +++ b/include/linux/usb/otg-fsm.h
>> @@ -21,21 +21,6 @@
>> #include <linux/mutex.h>
>> #include <linux/errno.h>
>>
>> -#undef VERBOSE
>> -
>> -#ifdef VERBOSE
>> -#define VDBG(fmt, args...) pr_debug("[%s] " fmt , \
>> - __func__, ## args)
>> -#else
>> -#define VDBG(stuff...) do {} while (0)
>> -#endif
>> -
>> -#ifdef VERBOSE
>> -#define MPC_LOC printk("Current Location [%s]:[%d]\n", __FILE__, __LINE__)
>> -#else
>> -#define MPC_LOC do {} while (0)
>> -#endif
>> -
>> #define PROTO_UNDEF (0)
>> #define PROTO_HOST (1)
>> #define PROTO_GADGET (2)
>> --
>> 2.1.0
>>
>
On Tue, Apr 14, 2015 at 01:41:52PM +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
>
> TODO:
> - sysfs interface to allow application inputs to OTG state machine
> - otg class?
>
> Signed-off-by: Roger Quadros <[email protected]>
> ---
> drivers/usb/Makefile | 1 +
> drivers/usb/common/Makefile | 1 +
> drivers/usb/common/usb-otg.c | 743 +++++++++++++++++++++++++++++++++++++++++++
> drivers/usb/common/usb-otg.h | 71 +++++
> drivers/usb/core/Kconfig | 8 +
> include/linux/usb/usb-otg.h | 94 ++++++
There is otg.h at include/linux/usb/, why create another usb-otg.h,
it may confuse the user in future
If I understand correct, in your plan, both DRD and OTG FSM devices will run
OTG state machine code, right?
Peter
> 6 files changed, 918 insertions(+)
> create mode 100644 drivers/usb/common/usb-otg.c
> create mode 100644 drivers/usb/common/usb-otg.h
> create mode 100644 include/linux/usb/usb-otg.h
>
> diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
> index 2f1e2aa..07f59a5 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_CORE) += common/
>
> obj-$(CONFIG_USBIP_CORE) += usbip/
> diff --git a/drivers/usb/common/Makefile b/drivers/usb/common/Makefile
> index ca2f8bd..573fc75 100644
> --- a/drivers/usb/common/Makefile
> +++ b/drivers/usb/common/Makefile
> @@ -7,3 +7,4 @@ 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_OTG_CORE) += usb-otg.o
> diff --git a/drivers/usb/common/usb-otg.c b/drivers/usb/common/usb-otg.c
> new file mode 100644
> index 0000000..e848e08
> --- /dev/null
> +++ b/drivers/usb/common/usb-otg.c
> @@ -0,0 +1,743 @@
> +/**
> + * 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/usb/usb-otg.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_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;
> +
> + /* 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);
> +
> +/**
> + * 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);
> +
> + 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 */
> + usb_add_hcd(otgd->primary_hcd.hcd, otgd->primary_hcd.irqnum,
> + otgd->primary_hcd.irqflags);
> + if (otgd->shared_hcd.hcd) {
> + usb_add_hcd(otgd->shared_hcd.hcd,
> + otgd->shared_hcd.irqnum,
> + otgd->shared_hcd.irqflags);
> + }
> + } else {
> + /* stop host */
> + if (otgd->shared_hcd.hcd)
> + usb_remove_hcd(otgd->shared_hcd.hcd);
> + usb_remove_hcd(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);
> +
> + usb_gadget_start(fsm->otg->gadget);
> + } else {
> + usb_gadget_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);
> +
> + if (otg_statemachine(&otgd->fsm)) {
> + /* state changed. any action ? */
> + }
> +}
> +
> +/**
> + * 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
> + *
> + * 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_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_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;
> + 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_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;
> + 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_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;
> + 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 cc0ced0..43a0d2d 100644
> --- a/drivers/usb/core/Kconfig
> +++ b/drivers/usb/core/Kconfig
> @@ -84,3 +84,11 @@ config USB_OTG_FSM
> Implements OTG Finite State Machine as specified in On-The-Go
> and Embedded Host Supplement to the USB Revision 2.0 Specification.
>
> +config USB_OTG_CORE
> + tristate "USB OTG core"
> + depends on USB
> + select USB_OTG_FSM
> + help
> + Standardize the way OTG is implemented on Linux. The OTG state
> + machine is instantiated here instead of being done in each controller
> + driver.
> diff --git a/include/linux/usb/usb-otg.h b/include/linux/usb/usb-otg.h
> new file mode 100644
> index 0000000..8987cd1
> --- /dev/null
> +++ b/include/linux/usb/usb-otg.h
> @@ -0,0 +1,94 @@
> +/**
> + * include/linux/usb/usb-otg.h - 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.
> + */
> +
> +#ifndef __LINUX_USB_OTG_CORE_H
> +#define __LINUX_USB_OTG_CORE_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>
> +
> +#if IS_ENABLED(CONFIG_USB_OTG_CORE)
> +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);
> +int usb_otg_unregister_hcd(struct usb_hcd *hcd);
> +int usb_otg_register_gadget(struct usb_gadget *gadget);
> +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);
> +bool usb_otg_gadget_can_start(struct usb_gadget *gadget);
> +struct device *usb_otg_fsm_to_dev(struct otg_fsm *fsm);
> +
> +#else /* CONFIG_USB_OTG_CORE */
> +
> +static inline struct otg_fsm *usb_otg_register(struct device *parent_dev,
> + struct otg_fsm_ops *fsm_ops)
> +{
> + 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)
> +{
> + return -ENOSYS;
> +}
> +
> +int usb_otg_unregister_hcd(struct usb_hcd *hcd)
> +{
> + return -ENOSYS;
> +}
> +
> +static inline int usb_otg_register_gadget(struct usb_gadget *gadget)
> +{
> + 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)
> +{
> +}
> +
> +int usb_otg_kick_fsm(struct device *hcd_gcd_device)
> +{
> + return -ENOSYS;
> +}
> +
> +bool usb_otg_gadget_can_start(struct usb_gadget *gadget)
> +{
> + return true;
> +}
> +
> +static inline struct device *usb_otg_fsm_to_dev(struct otg_fsm *fsm)
> +{
> + return NULL;
> +}
> +#endif /* CONFIG_USB_OTG_CORE */
> +
> +#endif /* __LINUX_USB_OTG_CORE */
> --
> 2.1.0
>
--
Best Regards,
Peter Chen
On Thu, Apr 16, 2015 at 02:58:20PM +0300, Roger Quadros wrote:
> On 16/04/15 14:36, Peter Chen wrote:
> > On Tue, Apr 14, 2015 at 01:41:49PM +0300, Roger Quadros wrote:
> >> Move the state_changed variable into struct otg_fsm
> >> so that we can support multiple instances.
> >
> > OTG device has only one port. See 3.1.1.
> > If you go to pass OTG Certification, the lab requires that there is only
> > one port at your board.
> >
>
> We're not breaking OTG compliance by this ;).
> Moreover this structure is not limited to OTG but for DRD (dual-role) use as well.
> We can have multiple DRD ports per board.
>
Ok, the code is ok for keeping multiple instances.
Peter
>
> >>
> >> 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 1736bbe..0caa37b 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));
> >> @@ -255,7 +253,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) {
> >> @@ -368,7 +366,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 c5b74c5..85a9150 100644
> >> --- a/include/linux/usb/otg-fsm.h
> >> +++ b/include/linux/usb/otg-fsm.h
> >> @@ -183,6 +183,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.0
> >>
> >
>
--
Best Regards,
Peter Chen
On 16/04/15 14:48, Peter Chen wrote:
> On Tue, Apr 14, 2015 at 01:41:51PM +0300, Roger Quadros wrote:
>> The OTG state machine needs a mechanism to start and
>> stop the gadget controller. Add usb_gadget_start()
>> and usb_gadget_stop().
>>
>> Signed-off-by: Roger Quadros <[email protected]>
>> ---
>> drivers/usb/gadget/udc/udc-core.c | 74 +++++++++++++++++++++++++++++++++++++++
>> include/linux/usb/gadget.h | 3 ++
>> 2 files changed, 77 insertions(+)
>>
>> diff --git a/drivers/usb/gadget/udc/udc-core.c b/drivers/usb/gadget/udc/udc-core.c
>> index 5a81cb0..3aa5dd5 100644
>> --- a/drivers/usb/gadget/udc/udc-core.c
>> +++ b/drivers/usb/gadget/udc/udc-core.c
>> @@ -187,6 +187,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);
>> }
>>
>> @@ -204,10 +205,83 @@ 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).
>> + */
>> +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_gadget_connect(udc->gadget);
>
> OTG FSM manages its pullup/pulldown itself through its fsm->ops->loc_conn
That API usb_gadget_udc_start() is used by DRD (dual-role) FSM as well
and it doesn't use ops->loc_conn. So i'm wondering how to make this work for
both.
I could probably remove the pull up control from usb_gadget_start/stop()
and move it into the DRD FSM.
cheers,
-roger
>
>> +
>> + mutex_unlock(&udc_lock);
>> +
>> + return ret;
>> +}
>> +EXPORT_SYMBOL_GPL(usb_gadget_start);
>> +
>> +/**
>> + * 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.
>> + */
>> +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;
>> +}
>> +EXPORT_SYMBOL_GPL(usb_gadget_stop);
>> +
>> +/**
>> * usb_udc_release - release the usb_udc struct
>> * @dev: the dev member within usb_udc
>> *
>> diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h
>> index e2f00fd..7ada7e6 100644
>> --- a/include/linux/usb/gadget.h
>> +++ b/include/linux/usb/gadget.h
>> @@ -922,6 +922,9 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver);
>> */
>> int usb_gadget_unregister_driver(struct usb_gadget_driver *driver);
>>
>> +int usb_gadget_start(struct usb_gadget *gadget);
>> +int usb_gadget_stop(struct usb_gadget *gadget);
>> +
>> extern int usb_add_gadget_udc_release(struct device *parent,
>> struct usb_gadget *gadget, void (*release)(struct device *dev));
>> extern int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget);
>> --
>> 2.1.0
>>
>
On Thu, Apr 16, 2015 at 02:59:12PM +0300, Roger Quadros wrote:
> On 16/04/15 14:41, Peter Chen wrote:
> > On Tue, Apr 14, 2015 at 01:41:50PM +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 moving the VDBG defination into the
> >> usb-otg-fsm.c file where it is used.
> >>
> >> 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/common/usb-otg-fsm.c | 9 +++++++++
> >> include/linux/usb/otg-fsm.h | 15 ---------------
> >> 2 files changed, 9 insertions(+), 15 deletions(-)
> >>
> >> diff --git a/drivers/usb/common/usb-otg-fsm.c b/drivers/usb/common/usb-otg-fsm.c
> >> index 0caa37b..35f311a 100644
> >> --- a/drivers/usb/common/usb-otg-fsm.c
> >> +++ b/drivers/usb/common/usb-otg-fsm.c
> >> @@ -30,6 +30,15 @@
> >> #include <linux/usb/otg.h>
> >> #include <linux/usb/otg-fsm.h>
> >>
> >> +#undef VDBG
> >> +
> >> +#ifdef VERBOSE
> >> +#define VDBG(fmt, args...) pr_debug("[%s] " fmt , \
> >> + __func__, ## args)
> >> +#else
> >> +#define VDBG(stuff...) do {} while (0)
> >> +#endif
> >> +
> >
> > It is obsolete too, we may use dev_vdbg instead of it.
>
> ok, we need to add 'struct device *' to 'struct otg_fsm' then.
>
I am not sure if we will keep struct device * for otg device at last,
if it is, you can use otg's.
Peter
> cheers,
> -roger
>
> >
> >> /* Change USB protocol when there is a protocol change */
> >> static int otg_set_protocol(struct otg_fsm *fsm, int protocol)
> >> {
> >> diff --git a/include/linux/usb/otg-fsm.h b/include/linux/usb/otg-fsm.h
> >> index 85a9150..73136aa 100644
> >> --- a/include/linux/usb/otg-fsm.h
> >> +++ b/include/linux/usb/otg-fsm.h
> >> @@ -21,21 +21,6 @@
> >> #include <linux/mutex.h>
> >> #include <linux/errno.h>
> >>
> >> -#undef VERBOSE
> >> -
> >> -#ifdef VERBOSE
> >> -#define VDBG(fmt, args...) pr_debug("[%s] " fmt , \
> >> - __func__, ## args)
> >> -#else
> >> -#define VDBG(stuff...) do {} while (0)
> >> -#endif
> >> -
> >> -#ifdef VERBOSE
> >> -#define MPC_LOC printk("Current Location [%s]:[%d]\n", __FILE__, __LINE__)
> >> -#else
> >> -#define MPC_LOC do {} while (0)
> >> -#endif
> >> -
> >> #define PROTO_UNDEF (0)
> >> #define PROTO_HOST (1)
> >> #define PROTO_GADGET (2)
> >> --
> >> 2.1.0
> >>
> >
>
--
Best Regards,
Peter Chen
On Thu, Apr 16, 2015 at 03:07:41PM +0300, Roger Quadros wrote:
> On 16/04/15 14:48, Peter Chen wrote:
> > On Tue, Apr 14, 2015 at 01:41:51PM +0300, Roger Quadros wrote:
> >> The OTG state machine needs a mechanism to start and
> >> stop the gadget controller. Add usb_gadget_start()
> >> and usb_gadget_stop().
> >>
> >> Signed-off-by: Roger Quadros <[email protected]>
> >> ---
> >> drivers/usb/gadget/udc/udc-core.c | 74 +++++++++++++++++++++++++++++++++++++++
> >> include/linux/usb/gadget.h | 3 ++
> >> 2 files changed, 77 insertions(+)
> >>
> >> diff --git a/drivers/usb/gadget/udc/udc-core.c b/drivers/usb/gadget/udc/udc-core.c
> >> index 5a81cb0..3aa5dd5 100644
> >> --- a/drivers/usb/gadget/udc/udc-core.c
> >> +++ b/drivers/usb/gadget/udc/udc-core.c
> >> @@ -187,6 +187,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);
> >> }
> >>
> >> @@ -204,10 +205,83 @@ 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).
> >> + */
> >> +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_gadget_connect(udc->gadget);
> >
> > OTG FSM manages its pullup/pulldown itself through its fsm->ops->loc_conn
>
> That API usb_gadget_udc_start() is used by DRD (dual-role) FSM as well
> and it doesn't use ops->loc_conn. So i'm wondering how to make this work for
> both.
>
> I could probably remove the pull up control from usb_gadget_start/stop()
> and move it into the DRD FSM.
>
Just like I comment your at your 5th patch, if we want both DRD and OTG FSM
devices run the same code. From our experience, it can cover both at
runtime if we can handle otg descriptor well (no otg descriptor for
DRD device).
Peter
>
> >
> >> +
> >> + mutex_unlock(&udc_lock);
> >> +
> >> + return ret;
> >> +}
> >> +EXPORT_SYMBOL_GPL(usb_gadget_start);
> >> +
> >> +/**
> >> + * 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.
> >> + */
> >> +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;
> >> +}
> >> +EXPORT_SYMBOL_GPL(usb_gadget_stop);
> >> +
> >> +/**
> >> * usb_udc_release - release the usb_udc struct
> >> * @dev: the dev member within usb_udc
> >> *
> >> diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h
> >> index e2f00fd..7ada7e6 100644
> >> --- a/include/linux/usb/gadget.h
> >> +++ b/include/linux/usb/gadget.h
> >> @@ -922,6 +922,9 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver);
> >> */
> >> int usb_gadget_unregister_driver(struct usb_gadget_driver *driver);
> >>
> >> +int usb_gadget_start(struct usb_gadget *gadget);
> >> +int usb_gadget_stop(struct usb_gadget *gadget);
> >> +
> >> extern int usb_add_gadget_udc_release(struct device *parent,
> >> struct usb_gadget *gadget, void (*release)(struct device *dev));
> >> extern int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget);
> >> --
> >> 2.1.0
> >>
> >
>
--
Best Regards,
Peter Chen
On 16/04/15 15:02, Peter Chen wrote:
> On Tue, Apr 14, 2015 at 01:41:52PM +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
>>
>> TODO:
>> - sysfs interface to allow application inputs to OTG state machine
>> - otg class?
>>
>> Signed-off-by: Roger Quadros <[email protected]>
>> ---
>> drivers/usb/Makefile | 1 +
>> drivers/usb/common/Makefile | 1 +
>> drivers/usb/common/usb-otg.c | 743 +++++++++++++++++++++++++++++++++++++++++++
>> drivers/usb/common/usb-otg.h | 71 +++++
>> drivers/usb/core/Kconfig | 8 +
>> include/linux/usb/usb-otg.h | 94 ++++++
>
> There is otg.h at include/linux/usb/, why create another usb-otg.h,
> it may confuse the user in future
I can reuse that file.
>
> If I understand correct, in your plan, both DRD and OTG FSM devices will run
> OTG state machine code, right?
That was my original plan but then I thought it unnecessary to initialize
all those OTG timers if only DRD operation is needed so I've implemented a
very simple DRD state machine. see patch 7.
cheers,
-roger
>
> Peter
>
>> 6 files changed, 918 insertions(+)
>> create mode 100644 drivers/usb/common/usb-otg.c
>> create mode 100644 drivers/usb/common/usb-otg.h
>> create mode 100644 include/linux/usb/usb-otg.h
>>
>> diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
>> index 2f1e2aa..07f59a5 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_CORE) += common/
>>
>> obj-$(CONFIG_USBIP_CORE) += usbip/
>> diff --git a/drivers/usb/common/Makefile b/drivers/usb/common/Makefile
>> index ca2f8bd..573fc75 100644
>> --- a/drivers/usb/common/Makefile
>> +++ b/drivers/usb/common/Makefile
>> @@ -7,3 +7,4 @@ 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_OTG_CORE) += usb-otg.o
>> diff --git a/drivers/usb/common/usb-otg.c b/drivers/usb/common/usb-otg.c
>> new file mode 100644
>> index 0000000..e848e08
>> --- /dev/null
>> +++ b/drivers/usb/common/usb-otg.c
>> @@ -0,0 +1,743 @@
>> +/**
>> + * 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/usb/usb-otg.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_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;
>> +
>> + /* 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);
>> +
>> +/**
>> + * 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);
>> +
>> + 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 */
>> + usb_add_hcd(otgd->primary_hcd.hcd, otgd->primary_hcd.irqnum,
>> + otgd->primary_hcd.irqflags);
>> + if (otgd->shared_hcd.hcd) {
>> + usb_add_hcd(otgd->shared_hcd.hcd,
>> + otgd->shared_hcd.irqnum,
>> + otgd->shared_hcd.irqflags);
>> + }
>> + } else {
>> + /* stop host */
>> + if (otgd->shared_hcd.hcd)
>> + usb_remove_hcd(otgd->shared_hcd.hcd);
>> + usb_remove_hcd(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);
>> +
>> + usb_gadget_start(fsm->otg->gadget);
>> + } else {
>> + usb_gadget_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);
>> +
>> + if (otg_statemachine(&otgd->fsm)) {
>> + /* state changed. any action ? */
>> + }
>> +}
>> +
>> +/**
>> + * 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
>> + *
>> + * 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_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_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;
>> + 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_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;
>> + 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_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;
>> + 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 cc0ced0..43a0d2d 100644
>> --- a/drivers/usb/core/Kconfig
>> +++ b/drivers/usb/core/Kconfig
>> @@ -84,3 +84,11 @@ config USB_OTG_FSM
>> Implements OTG Finite State Machine as specified in On-The-Go
>> and Embedded Host Supplement to the USB Revision 2.0 Specification.
>>
>> +config USB_OTG_CORE
>> + tristate "USB OTG core"
>> + depends on USB
>> + select USB_OTG_FSM
>> + help
>> + Standardize the way OTG is implemented on Linux. The OTG state
>> + machine is instantiated here instead of being done in each controller
>> + driver.
>> diff --git a/include/linux/usb/usb-otg.h b/include/linux/usb/usb-otg.h
>> new file mode 100644
>> index 0000000..8987cd1
>> --- /dev/null
>> +++ b/include/linux/usb/usb-otg.h
>> @@ -0,0 +1,94 @@
>> +/**
>> + * include/linux/usb/usb-otg.h - 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.
>> + */
>> +
>> +#ifndef __LINUX_USB_OTG_CORE_H
>> +#define __LINUX_USB_OTG_CORE_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>
>> +
>> +#if IS_ENABLED(CONFIG_USB_OTG_CORE)
>> +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);
>> +int usb_otg_unregister_hcd(struct usb_hcd *hcd);
>> +int usb_otg_register_gadget(struct usb_gadget *gadget);
>> +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);
>> +bool usb_otg_gadget_can_start(struct usb_gadget *gadget);
>> +struct device *usb_otg_fsm_to_dev(struct otg_fsm *fsm);
>> +
>> +#else /* CONFIG_USB_OTG_CORE */
>> +
>> +static inline struct otg_fsm *usb_otg_register(struct device *parent_dev,
>> + struct otg_fsm_ops *fsm_ops)
>> +{
>> + 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)
>> +{
>> + return -ENOSYS;
>> +}
>> +
>> +int usb_otg_unregister_hcd(struct usb_hcd *hcd)
>> +{
>> + return -ENOSYS;
>> +}
>> +
>> +static inline int usb_otg_register_gadget(struct usb_gadget *gadget)
>> +{
>> + 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)
>> +{
>> +}
>> +
>> +int usb_otg_kick_fsm(struct device *hcd_gcd_device)
>> +{
>> + return -ENOSYS;
>> +}
>> +
>> +bool usb_otg_gadget_can_start(struct usb_gadget *gadget)
>> +{
>> + return true;
>> +}
>> +
>> +static inline struct device *usb_otg_fsm_to_dev(struct otg_fsm *fsm)
>> +{
>> + return NULL;
>> +}
>> +#endif /* CONFIG_USB_OTG_CORE */
>> +
>> +#endif /* __LINUX_USB_OTG_CORE */
>> --
>> 2.1.0
>>
>
On Tue, Apr 14, 2015 at 01:41:53PM +0300, Roger Quadros wrote:
> 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_add/remove_hcd() for use by OTG core.
> These functions actually add/remove the HCD.
Would you please explain why additional _usb_add/remove_hcd are needed?
Peter
>
> Signed-off-by: Roger Quadros <[email protected]>
> ---
> drivers/usb/common/usb-otg.c | 14 +++++++-------
> drivers/usb/core/hcd.c | 24 ++++++++++++++++++++++--
> include/linux/usb/hcd.h | 3 +++
> 3 files changed, 32 insertions(+), 9 deletions(-)
>
> diff --git a/drivers/usb/common/usb-otg.c b/drivers/usb/common/usb-otg.c
> index e848e08..860e2e7 100644
> --- a/drivers/usb/common/usb-otg.c
> +++ b/drivers/usb/common/usb-otg.c
> @@ -198,18 +198,18 @@ static int usb_otg_start_host(struct otg_fsm *fsm, int on)
> otgd->start_host(fsm, on);
>
> /* start host */
> - usb_add_hcd(otgd->primary_hcd.hcd, otgd->primary_hcd.irqnum,
> - otgd->primary_hcd.irqflags);
> + _usb_add_hcd(otgd->primary_hcd.hcd, otgd->primary_hcd.irqnum,
> + otgd->primary_hcd.irqflags);
> if (otgd->shared_hcd.hcd) {
> - usb_add_hcd(otgd->shared_hcd.hcd,
> - otgd->shared_hcd.irqnum,
> - otgd->shared_hcd.irqflags);
> + _usb_add_hcd(otgd->shared_hcd.hcd,
> + otgd->shared_hcd.irqnum,
> + otgd->shared_hcd.irqflags);
> }
> } else {
> /* stop host */
> if (otgd->shared_hcd.hcd)
> - usb_remove_hcd(otgd->shared_hcd.hcd);
> - usb_remove_hcd(otgd->primary_hcd.hcd);
> + _usb_remove_hcd(otgd->shared_hcd.hcd);
> + _usb_remove_hcd(otgd->primary_hcd.hcd);
>
> /* OTG device operations */
> if (otgd->start_host)
> diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
> index 45a915c..9a9c0f7 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/usb-otg.h>
>
> #include "usb.h"
>
> @@ -2622,7 +2623,7 @@ 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,
> +int _usb_add_hcd(struct usb_hcd *hcd,
> unsigned int irqnum, unsigned long irqflags)
> {
> int retval;
> @@ -2827,6 +2828,17 @@ err_phy:
> }
> return retval;
> }
> +EXPORT_SYMBOL_GPL(_usb_add_hcd);
> +
> +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))
> + return _usb_add_hcd(hcd, irqnum, irqflags);
> +
> + return 0;
> +}
> EXPORT_SYMBOL_GPL(usb_add_hcd);
>
> /**
> @@ -2837,7 +2849,7 @@ EXPORT_SYMBOL_GPL(usb_add_hcd);
> * 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)
> +void _usb_remove_hcd(struct usb_hcd *hcd)
> {
> struct usb_device *rhdev = hcd->self.root_hub;
>
> @@ -2911,6 +2923,14 @@ void usb_remove_hcd(struct usb_hcd *hcd)
>
> usb_put_invalidate_rhdev(hcd);
> }
> +EXPORT_SYMBOL_GPL(_usb_remove_hcd);
> +
> +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_remove_hcd(hcd);
> +}
> EXPORT_SYMBOL_GPL(usb_remove_hcd);
>
> void
> diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h
> index 68b1e83..7993ae7 100644
> --- a/include/linux/usb/hcd.h
> +++ b/include/linux/usb/hcd.h
> @@ -433,7 +433,10 @@ extern void usb_put_hcd(struct usb_hcd *hcd);
> extern int usb_hcd_is_primary_hcd(struct usb_hcd *hcd);
> extern int usb_add_hcd(struct usb_hcd *hcd,
> unsigned int irqnum, unsigned long irqflags);
> +extern int _usb_add_hcd(struct usb_hcd *hcd,
> + unsigned int irqnum, unsigned long irqflags);
> extern void usb_remove_hcd(struct usb_hcd *hcd);
> +extern void _usb_remove_hcd(struct usb_hcd *hcd);
> extern int usb_hcd_find_raw_port_number(struct usb_hcd *hcd, int port1);
>
> struct platform_device;
> --
> 2.1.0
>
--
Best Regards,
Peter Chen
On Tue, Apr 14, 2015 at 01:41:55PM +0300, Roger Quadros wrote:
> 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 | 11 ++++++++++-
> 1 file changed, 10 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
> index d7c3d5a..ab0d498 100644
> --- a/drivers/usb/core/hub.c
> +++ b/drivers/usb/core/hub.c
> @@ -22,6 +22,7 @@
> #include <linux/usb/hcd.h>
> #include <linux/usb/otg.h>
> #include <linux/usb/quirks.h>
> +#include <linux/usb/usb-otg.h>
> #include <linux/workqueue.h>
> #include <linux/mutex.h>
> #include <linux/random.h>
> @@ -2270,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);
> }
> }
> }
> @@ -4244,8 +4248,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.0
>
Since the fsm has already kicked, the only thing we need is update
fsm.a_set_b_hnp_enable, but this flag is missing at current fsm
structure.
--
Best Regards,
Peter Chen
On 17/04/15 05:18, Peter Chen wrote:
> On Tue, Apr 14, 2015 at 01:41:53PM +0300, Roger Quadros wrote:
>> 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_add/remove_hcd() for use by OTG core.
>> These functions actually add/remove the HCD.
>
> Would you please explain why additional _usb_add/remove_hcd are needed?
It is to differentiate if the add/remove_hcd was called by the
HCD drivers or by the OTG core as we want to behave a bit differently
in both cases.
When called by HCD drivers, we want to defer the add/remove if it is an
OTG device. When called by OTG core, we don't want to defer the add/remove.
HCD drivers use usb_add/remove_hcd()
OTG core uses _usb_add/remove_hcd()
cheers,
-roger
>
> Peter
>>
>> Signed-off-by: Roger Quadros <[email protected]>
>> ---
>> drivers/usb/common/usb-otg.c | 14 +++++++-------
>> drivers/usb/core/hcd.c | 24 ++++++++++++++++++++++--
>> include/linux/usb/hcd.h | 3 +++
>> 3 files changed, 32 insertions(+), 9 deletions(-)
>>
>> diff --git a/drivers/usb/common/usb-otg.c b/drivers/usb/common/usb-otg.c
>> index e848e08..860e2e7 100644
>> --- a/drivers/usb/common/usb-otg.c
>> +++ b/drivers/usb/common/usb-otg.c
>> @@ -198,18 +198,18 @@ static int usb_otg_start_host(struct otg_fsm *fsm, int on)
>> otgd->start_host(fsm, on);
>>
>> /* start host */
>> - usb_add_hcd(otgd->primary_hcd.hcd, otgd->primary_hcd.irqnum,
>> - otgd->primary_hcd.irqflags);
>> + _usb_add_hcd(otgd->primary_hcd.hcd, otgd->primary_hcd.irqnum,
>> + otgd->primary_hcd.irqflags);
>> if (otgd->shared_hcd.hcd) {
>> - usb_add_hcd(otgd->shared_hcd.hcd,
>> - otgd->shared_hcd.irqnum,
>> - otgd->shared_hcd.irqflags);
>> + _usb_add_hcd(otgd->shared_hcd.hcd,
>> + otgd->shared_hcd.irqnum,
>> + otgd->shared_hcd.irqflags);
>> }
>> } else {
>> /* stop host */
>> if (otgd->shared_hcd.hcd)
>> - usb_remove_hcd(otgd->shared_hcd.hcd);
>> - usb_remove_hcd(otgd->primary_hcd.hcd);
>> + _usb_remove_hcd(otgd->shared_hcd.hcd);
>> + _usb_remove_hcd(otgd->primary_hcd.hcd);
>>
>> /* OTG device operations */
>> if (otgd->start_host)
>> diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
>> index 45a915c..9a9c0f7 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/usb-otg.h>
>>
>> #include "usb.h"
>>
>> @@ -2622,7 +2623,7 @@ 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,
>> +int _usb_add_hcd(struct usb_hcd *hcd,
>> unsigned int irqnum, unsigned long irqflags)
>> {
>> int retval;
>> @@ -2827,6 +2828,17 @@ err_phy:
>> }
>> return retval;
>> }
>> +EXPORT_SYMBOL_GPL(_usb_add_hcd);
>> +
>> +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))
>> + return _usb_add_hcd(hcd, irqnum, irqflags);
>> +
>> + return 0;
>> +}
>> EXPORT_SYMBOL_GPL(usb_add_hcd);
>>
>> /**
>> @@ -2837,7 +2849,7 @@ EXPORT_SYMBOL_GPL(usb_add_hcd);
>> * 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)
>> +void _usb_remove_hcd(struct usb_hcd *hcd)
>> {
>> struct usb_device *rhdev = hcd->self.root_hub;
>>
>> @@ -2911,6 +2923,14 @@ void usb_remove_hcd(struct usb_hcd *hcd)
>>
>> usb_put_invalidate_rhdev(hcd);
>> }
>> +EXPORT_SYMBOL_GPL(_usb_remove_hcd);
>> +
>> +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_remove_hcd(hcd);
>> +}
>> EXPORT_SYMBOL_GPL(usb_remove_hcd);
>>
>> void
>> diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h
>> index 68b1e83..7993ae7 100644
>> --- a/include/linux/usb/hcd.h
>> +++ b/include/linux/usb/hcd.h
>> @@ -433,7 +433,10 @@ extern void usb_put_hcd(struct usb_hcd *hcd);
>> extern int usb_hcd_is_primary_hcd(struct usb_hcd *hcd);
>> extern int usb_add_hcd(struct usb_hcd *hcd,
>> unsigned int irqnum, unsigned long irqflags);
>> +extern int _usb_add_hcd(struct usb_hcd *hcd,
>> + unsigned int irqnum, unsigned long irqflags);
>> extern void usb_remove_hcd(struct usb_hcd *hcd);
>> +extern void _usb_remove_hcd(struct usb_hcd *hcd);
>> extern int usb_hcd_find_raw_port_number(struct usb_hcd *hcd, int port1);
>>
>> struct platform_device;
>> --
>> 2.1.0
>>
>
On 17/04/15 05:28, Peter Chen wrote:
> On Tue, Apr 14, 2015 at 01:41:55PM +0300, Roger Quadros wrote:
>> 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 | 11 ++++++++++-
>> 1 file changed, 10 insertions(+), 1 deletion(-)
>>
>> diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
>> index d7c3d5a..ab0d498 100644
>> --- a/drivers/usb/core/hub.c
>> +++ b/drivers/usb/core/hub.c
>> @@ -22,6 +22,7 @@
>> #include <linux/usb/hcd.h>
>> #include <linux/usb/otg.h>
>> #include <linux/usb/quirks.h>
>> +#include <linux/usb/usb-otg.h>
>> #include <linux/workqueue.h>
>> #include <linux/mutex.h>
>> #include <linux/random.h>
>> @@ -2270,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);
>> }
>> }
>> }
>> @@ -4244,8 +4248,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.0
>>
>
> Since the fsm has already kicked, the only thing we need is update
> fsm.a_set_b_hnp_enable, but this flag is missing at current fsm
> structure.
Every time the input changes, the fsm has to be re-kicked.
Instead of fsm.a_set_b_hnp_enable we use fsm->otg->host->b_hnp_enable
in the fsm.
USB host stack doesn't have access to fsm structure but the OTG state
machine has access to hcd.
cheers,
-roger
On 16/04/15 14:32, Peter Chen wrote:
> On Tue, Apr 14, 2015 at 01:41:48PM +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 | 80 +++++++++++++++++++++++++++++++++++++++++----
>> 1 file changed, 73 insertions(+), 7 deletions(-)
>>
>> diff --git a/include/linux/usb/otg-fsm.h b/include/linux/usb/otg-fsm.h
>> index b6ba1bf..c5b74c5 100644
>> --- a/include/linux/usb/otg-fsm.h
>> +++ b/include/linux/usb/otg-fsm.h
>> @@ -57,37 +57,103 @@ 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
>
> What's the intention you leave two spaces at above line?
It is just to indicate the subgroup. i.e.
1) common inputs
2) a-device inputs
3) b-device inputs, etc
>
>> + * @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
>> + * FIXME: must be set by host/hub driver
>
> Yes, according to pid/vid pair.
>
>> + *
>> + * 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
>> + * @a_sess_vld: ??
>
> See OTG spec 1.3, and obsolete now.
OK. I will mark it obsolete.
>
>> + * @b_bus_suspend: ??
>> + * @b_bus_resume: ??
>
> They are used at current otg fsm, just for B-host.
These 2 are also present only in otg v1.3 spec and absent in v2.0 spec.
b_bus_suspend has been replaced by a_bidl_adis_tmout to transition from a_peripheral
to a_wait_bcon.
b_bus_resume has been replaced by a_bus_req to transition between a_suspend and a_host.
So I'll mark them as obsolete as well.
>
>> + *
>> + * 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.
>> + * FIXME: OTG fsm uses otg->host->b_hnp_enable instead
>
> a_set_b_hnp_en is for otg 2.0 and it should instead of
> otg->host->b_hnp_enable at otg fsm.
both are the same thing. Just that there is no way for HCD drivers to directly access
otg_fsm data structure so we rely on otg->host->b_hnp_enable.
>
>> + * 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
>> + * FIXME: OTG fsm uses otg->gadget->b_hnp_enable instead
>
> Yes
>
>> + * 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;
>> @@ -95,7 +161,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.0
>>
>
cheers,
-roger
On Fri, 17 Apr 2015, Roger Quadros wrote:
> On 17/04/15 05:18, Peter Chen wrote:
> > On Tue, Apr 14, 2015 at 01:41:53PM +0300, Roger Quadros wrote:
> >> 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_add/remove_hcd() for use by OTG core.
> >> These functions actually add/remove the HCD.
> >
> > Would you please explain why additional _usb_add/remove_hcd are needed?
>
> It is to differentiate if the add/remove_hcd was called by the
> HCD drivers or by the OTG core as we want to behave a bit differently
> in both cases.
> When called by HCD drivers, we want to defer the add/remove if it is an
> OTG device. When called by OTG core, we don't want to defer the add/remove.
I don't understand this. Why do you want to defer the add/remove if
the device is OTG? Don't host controller drivers expect these things
to execute synchronously?
For example, what happens if you rmmod the HCD? If the remove call
gets deferred, then when it finally runs it will try to call back into
HCD code that has already been unloaded from memory!
> HCD drivers use usb_add/remove_hcd()
> OTG core uses _usb_add/remove_hcd()
How about a more explicit naming scheme?
HC drivers use usb_add/remove_hcd()
OTG core uses usb_otg_add/remove_hcd()
Alan Stern
On Tue, Apr 14, 2015 at 01:41:47PM +0300, Roger Quadros wrote:
> This is an attempt to centralize OTG/Dual-role functionality in the kernel.
> As of now I've got Dual-role functionality working pretty reliably on
> dra7-evm. xhci side of things for OTG/DRD use are fixed in
> http://thread.gmane.org/gmane.linux.kernel/1923161
Hi Roger,
Currently, there are two main problems for DRD/OTG framework.
- For multi-platform supports, we may define CONFIG_USB_OTG, but the
gadget should not add its otg descriptor to its configuration
descriptors if it does not support one of otg features (SRP/HNP/ADP).
Macpaul Lin's patch set [1] is the right way to do it.
- We are lack of framework to handle OTG (DRD) switch, it is great you
are designing it. The main problem for this framework is how to handle
DRD/OTG FSM unify. My thought is we add two paths for them separate.
For easy, I suggest if the platform supports one of otg features, then
it goes to fully otg fsm, else it goes to simply otg fsm (like your drd
fsm). If you agree with it too, you may not need to add another "dr_mode"
value.
Peter
[1] http://www.spinics.net/lists/linux-usb/msg123927.html
>
> Changelog:
> ---------
> 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);
> 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);
> 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);
>
> '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.
> 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.
>
> Pending items:
> - We probably need a otg class.
> - sysfs interface for application OTG inputs and OTG status information
> - resolve symbol dependency for module use.
>
> --
> cheers,
> -roger
>
> Roger Quadros (13):
> usb: otg-fsm: Add documentation for struct otg_fsm
> usb: otg-fsm: support multiple instances
> usb: otg-fsm: Prevent build warning "VDBG" redefined
> usb: gadget: add usb_gadget_start/stop()
> usb: otg: add OTG core
> usb: hcd: Add hcd add/remove functions for OTG use
> usb: otg: Add dual-role device (DRD) support
> usb: otg: hub: Notify OTG fsm when A device sets b_hnp_enable
> usb: gadget: udc: adapt to OTG
> udc-core: fix lock circular dependency on udc_lock
> usb: add "dual-role" mode to dr_mode device tree helper
> usb: dwc3: add dual-role support
> ARM: dts: dra7x-evm: Enable dual-role for usb1
>
> Documentation/devicetree/bindings/usb/generic.txt | 2 +-
> arch/arm/boot/dts/dra7-evm.dts | 2 +-
> arch/arm/boot/dts/dra72-evm.dts | 2 +-
> drivers/usb/Makefile | 1 +
> drivers/usb/common/Makefile | 1 +
> drivers/usb/common/common.c | 1 +
> drivers/usb/common/usb-otg-fsm.c | 19 +-
> drivers/usb/common/usb-otg.c | 902 ++++++++++++++++++++++
> drivers/usb/common/usb-otg.h | 71 ++
> drivers/usb/core/Kconfig | 8 +
> drivers/usb/core/hcd.c | 24 +-
> drivers/usb/core/hub.c | 11 +-
> drivers/usb/dwc3/core.c | 146 +++-
> drivers/usb/dwc3/core.h | 6 +
> drivers/usb/dwc3/platform_data.h | 1 +
> drivers/usb/gadget/udc/udc-core.c | 119 ++-
> include/linux/usb/gadget.h | 3 +
> include/linux/usb/hcd.h | 3 +
> include/linux/usb/otg-fsm.h | 104 ++-
> include/linux/usb/otg.h | 1 +
> include/linux/usb/usb-otg.h | 95 +++
> 21 files changed, 1476 insertions(+), 46 deletions(-)
> create mode 100644 drivers/usb/common/usb-otg.c
> create mode 100644 drivers/usb/common/usb-otg.h
> create mode 100644 include/linux/usb/usb-otg.h
>
> --
> 2.1.0
>
--
Best Regards,
Peter Chen
On 17/04/15 17:03, Alan Stern wrote:
> On Fri, 17 Apr 2015, Roger Quadros wrote:
>
>> On 17/04/15 05:18, Peter Chen wrote:
>>> On Tue, Apr 14, 2015 at 01:41:53PM +0300, Roger Quadros wrote:
>>>> 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_add/remove_hcd() for use by OTG core.
>>>> These functions actually add/remove the HCD.
>>>
>>> Would you please explain why additional _usb_add/remove_hcd are needed?
>>
>> It is to differentiate if the add/remove_hcd was called by the
>> HCD drivers or by the OTG core as we want to behave a bit differently
>> in both cases.
>> When called by HCD drivers, we want to defer the add/remove if it is an
>> OTG device. When called by OTG core, we don't want to defer the add/remove.
>
> I don't understand this. Why do you want to defer the add/remove if
> the device is OTG? Don't host controller drivers expect these things
> to execute synchronously?
Sorry for the wrong information. We actually defer only the add as the
OTG state machine might not yet be in Host ready mode.
The remove is always synchronous and we ensure that the HCD is removed
when usb_otg_unregister_hcd() returns.
>
> For example, what happens if you rmmod the HCD? If the remove call
> gets deferred, then when it finally runs it will try to call back into
> HCD code that has already been unloaded from memory!
>
>> HCD drivers use usb_add/remove_hcd()
>> OTG core uses _usb_add/remove_hcd()
>
> How about a more explicit naming scheme?
>
> HC drivers use usb_add/remove_hcd()
> OTG core uses usb_otg_add/remove_hcd()
Yes, this is better.
cheers,
-roger
Hi Peter,
On 20/04/15 06:05, Peter Chen wrote:
> On Tue, Apr 14, 2015 at 01:41:47PM +0300, Roger Quadros wrote:
>> This is an attempt to centralize OTG/Dual-role functionality in the kernel.
>> As of now I've got Dual-role functionality working pretty reliably on
>> dra7-evm. xhci side of things for OTG/DRD use are fixed in
>> http://thread.gmane.org/gmane.linux.kernel/1923161
>
> Hi Roger,
>
> Currently, there are two main problems for DRD/OTG framework.
>
> - For multi-platform supports, we may define CONFIG_USB_OTG, but the
> gadget should not add its otg descriptor to its configuration
> descriptors if it does not support one of otg features (SRP/HNP/ADP).
> Macpaul Lin's patch set [1] is the right way to do it.
Agreed. That check (whether OTG descriptor can be added and which version
of it) has to be done at runtime and it must be added only if hardware
supports OTG _and_ kernel OTG support is enabled.
> - We are lack of framework to handle OTG (DRD) switch, it is great you
> are designing it. The main problem for this framework is how to handle
> DRD/OTG FSM unify. My thought is we add two paths for them separate.
> For easy, I suggest if the platform supports one of otg features, then
> it goes to fully otg fsm, else it goes to simply otg fsm (like your drd
> fsm). If you agree with it too, you may not need to add another "dr_mode"
> value.
It would be nice that way but unfortunately it does't work in all cases.
e.g. What if the SoC itself supports all OTG features but the board is not
designed for OTG. Or the product designer simply is not interested in full OTG
support but just dual-role. So we need some flexibility for the
device tree/platform-data to specify that. This is where a new
"dr_mode" == "dual-role" is needed.
cheers,
-roger
On Mon, 20 Apr 2015, Roger Quadros wrote:
> > I don't understand this. Why do you want to defer the add/remove if
> > the device is OTG? Don't host controller drivers expect these things
> > to execute synchronously?
>
> Sorry for the wrong information. We actually defer only the add as the
> OTG state machine might not yet be in Host ready mode.
> The remove is always synchronous and we ensure that the HCD is removed
> when usb_otg_unregister_hcd() returns.
That's okay then, but it does leave one potential hole. What happens
if usb_add_hcd() is deferred for so long that usb_remove_hcd() gets
called before the add can complete?
Alan Stern
>
> On 20/04/15 06:05, Peter Chen wrote:
> > On Tue, Apr 14, 2015 at 01:41:47PM +0300, Roger Quadros wrote:
> >> This is an attempt to centralize OTG/Dual-role functionality in the kernel.
> >> As of now I've got Dual-role functionality working pretty reliably on
> >> dra7-evm. xhci side of things for OTG/DRD use are fixed in
> >> http://thread.gmane.org/gmane.linux.kernel/1923161
> >
> > Hi Roger,
> >
> > Currently, there are two main problems for DRD/OTG framework.
> >
> > - For multi-platform supports, we may define CONFIG_USB_OTG, but the
> > gadget should not add its otg descriptor to its configuration
> > descriptors if it does not support one of otg features (SRP/HNP/ADP).
> > Macpaul Lin's patch set [1] is the right way to do it.
>
> Agreed. That check (whether OTG descriptor can be added and which version
> of it) has to be done at runtime and it must be added only if hardware supports
> OTG _and_ kernel OTG support is enabled.
>
Ok, let's put this patch set in mainline first, since your patch set may need some
information from it.
> > - We are lack of framework to handle OTG (DRD) switch, it is great you
> > are designing it. The main problem for this framework is how to handle
> > DRD/OTG FSM unify. My thought is we add two paths for them separate.
> > For easy, I suggest if the platform supports one of otg features, then
> > it goes to fully otg fsm, else it goes to simply otg fsm (like your
> > drd fsm). If you agree with it too, you may not need to add another
> "dr_mode"
> > value.
>
> It would be nice that way but unfortunately it does't work in all cases.
> e.g. What if the SoC itself supports all OTG features but the board is not
> designed for OTG. Or the product designer simply is not interested in full OTG
> support but just dual-role. So we need some flexibility for the device
> tree/platform-data to specify that. This is where a new "dr_mode" == "dual-
> role" is needed.
>
Since "dr_mode" has been widely used now, if we add a new property for it, we
need to change all drivers.
Your OTG/DRD framework needs to (partial) use otg fsm, and we will not teach old
driver to use it since there are some driver related stuffs.
SRP/HNP/ADP support can be board level capabilities, and we may consider the
otg device which does not support otg fsm (hardware finishes fsm). So I suggest
we have below properties at dts:
- otg-support /* fully otg support */
- otg-fsm-support /* fully otg fsm support */
- otg-ver /* eh & otg supplement version */
- adp-support /* board adp support */
- srp-support /* board srp support */
- hnp-support /* board hnp support */
Currently, if CONFIG_USB_OTG and CONFIG_USB_OTG_FSM are enabled, we will
have otg fsm code (usb-otg-fsm.c).
if (otg-support & otg-fsm-support)
this device has fully otg support, and will follow full otg fsm transitions.
else
this device is drd, and will follow simple otg fsm transtions.
Peter
On 20/04/15 16:56, Alan Stern wrote:
> On Mon, 20 Apr 2015, Roger Quadros wrote:
>
>>> I don't understand this. Why do you want to defer the add/remove if
>>> the device is OTG? Don't host controller drivers expect these things
>>> to execute synchronously?
>>
>> Sorry for the wrong information. We actually defer only the add as the
>> OTG state machine might not yet be in Host ready mode.
>> The remove is always synchronous and we ensure that the HCD is removed
>> when usb_otg_unregister_hcd() returns.
>
> That's okay then, but it does leave one potential hole. What happens
> if usb_add_hcd() is deferred for so long that usb_remove_hcd() gets
> called before the add can complete?
We keep track of the HCD state in the OTG state machine and if it was not
yet added then _usb_remove_hcd() is not called during usb_otg_unregister_hcd()
in the usb_remove_hcd() call.
cheers,
-roger
On 21/04/15 09:04, Peter Chen wrote:
>
>>
>> On 20/04/15 06:05, Peter Chen wrote:
>>> On Tue, Apr 14, 2015 at 01:41:47PM +0300, Roger Quadros wrote:
>>>> This is an attempt to centralize OTG/Dual-role functionality in the kernel.
>>>> As of now I've got Dual-role functionality working pretty reliably on
>>>> dra7-evm. xhci side of things for OTG/DRD use are fixed in
>>>> http://thread.gmane.org/gmane.linux.kernel/1923161
>>>
>>> Hi Roger,
>>>
>>> Currently, there are two main problems for DRD/OTG framework.
>>>
>>> - For multi-platform supports, we may define CONFIG_USB_OTG, but the
>>> gadget should not add its otg descriptor to its configuration
>>> descriptors if it does not support one of otg features (SRP/HNP/ADP).
>>> Macpaul Lin's patch set [1] is the right way to do it.
>>
>> Agreed. That check (whether OTG descriptor can be added and which version
>> of it) has to be done at runtime and it must be added only if hardware supports
>> OTG _and_ kernel OTG support is enabled.
>>
>
> Ok, let's put this patch set in mainline first, since your patch set may need some
> information from it.
>
>>> - We are lack of framework to handle OTG (DRD) switch, it is great you
>>> are designing it. The main problem for this framework is how to handle
>>> DRD/OTG FSM unify. My thought is we add two paths for them separate.
>>> For easy, I suggest if the platform supports one of otg features, then
>>> it goes to fully otg fsm, else it goes to simply otg fsm (like your
>>> drd fsm). If you agree with it too, you may not need to add another
>> "dr_mode"
>>> value.
>>
>> It would be nice that way but unfortunately it does't work in all cases.
>> e.g. What if the SoC itself supports all OTG features but the board is not
>> designed for OTG. Or the product designer simply is not interested in full OTG
>> support but just dual-role. So we need some flexibility for the device
>> tree/platform-data to specify that. This is where a new "dr_mode" == "dual-
>> role" is needed.
>>
>
> Since "dr_mode" has been widely used now, if we add a new property for it, we
> need to change all drivers.
>
> Your OTG/DRD framework needs to (partial) use otg fsm, and we will not teach old
> driver to use it since there are some driver related stuffs.
fair enough. Let's not change dr_mode then and decide based on other parameters.
>
> SRP/HNP/ADP support can be board level capabilities, and we may consider the
> otg device which does not support otg fsm (hardware finishes fsm). So I suggest
> we have below properties at dts:
>
> - otg-support /* fully otg support */
> - otg-fsm-support /* fully otg fsm support */
what is the difference between otg-support and otg-fsm-support?
> - otg-ver /* eh & otg supplement version */
we can get otg version from the OTG controller. What exactly is the
otg-ver in dts for?
> - adp-support /* board adp support */
> - srp-support /* board srp support */
> - hnp-support /* board hnp support */
So if these options are not provided in DTS but the OTG core supports them then
we keep the respective feature disabled?
Won't this need dts change for existing boards?
Instead how about having disable flags instead.
- adp-disable /* board doesn't support adp */
- srp-disable /* board doesn't support srp */
- hnp-disable /* board doesn't support hnp */
Now, if the flags are not provided in dts we use the OTG core's flags.
>
> Currently, if CONFIG_USB_OTG and CONFIG_USB_OTG_FSM are enabled, we will
> have otg fsm code (usb-otg-fsm.c).
>
> if (otg-support & otg-fsm-support)
> this device has fully otg support, and will follow full otg fsm transitions.
> else
> this device is drd, and will follow simple otg fsm transtions.
>
cheers,
-roger
On Tue, Apr 21, 2015 at 10:34:01AM +0300, Roger Quadros wrote:
> On 21/04/15 09:04, Peter Chen wrote:
> >
> >>
> >> On 20/04/15 06:05, Peter Chen wrote:
> >>> On Tue, Apr 14, 2015 at 01:41:47PM +0300, Roger Quadros wrote:
> >>>> This is an attempt to centralize OTG/Dual-role functionality in the kernel.
> >>>> As of now I've got Dual-role functionality working pretty reliably on
> >>>> dra7-evm. xhci side of things for OTG/DRD use are fixed in
> >>>> http://thread.gmane.org/gmane.linux.kernel/1923161
> >>>
> >>> Hi Roger,
> >>>
> >>> Currently, there are two main problems for DRD/OTG framework.
> >>>
> >>> - For multi-platform supports, we may define CONFIG_USB_OTG, but the
> >>> gadget should not add its otg descriptor to its configuration
> >>> descriptors if it does not support one of otg features (SRP/HNP/ADP).
> >>> Macpaul Lin's patch set [1] is the right way to do it.
> >>
> >> Agreed. That check (whether OTG descriptor can be added and which version
> >> of it) has to be done at runtime and it must be added only if hardware supports
> >> OTG _and_ kernel OTG support is enabled.
> >>
> >
> > Ok, let's put this patch set in mainline first, since your patch set may need some
> > information from it.
> >
> >>> - We are lack of framework to handle OTG (DRD) switch, it is great you
> >>> are designing it. The main problem for this framework is how to handle
> >>> DRD/OTG FSM unify. My thought is we add two paths for them separate.
> >>> For easy, I suggest if the platform supports one of otg features, then
> >>> it goes to fully otg fsm, else it goes to simply otg fsm (like your
> >>> drd fsm). If you agree with it too, you may not need to add another
> >> "dr_mode"
> >>> value.
> >>
> >> It would be nice that way but unfortunately it does't work in all cases.
> >> e.g. What if the SoC itself supports all OTG features but the board is not
> >> designed for OTG. Or the product designer simply is not interested in full OTG
> >> support but just dual-role. So we need some flexibility for the device
> >> tree/platform-data to specify that. This is where a new "dr_mode" == "dual-
> >> role" is needed.
> >>
> >
> > Since "dr_mode" has been widely used now, if we add a new property for it, we
> > need to change all drivers.
> >
> > Your OTG/DRD framework needs to (partial) use otg fsm, and we will not teach old
> > driver to use it since there are some driver related stuffs.
>
> fair enough. Let's not change dr_mode then and decide based on other parameters.
>
> >
> > SRP/HNP/ADP support can be board level capabilities, and we may consider the
> > otg device which does not support otg fsm (hardware finishes fsm). So I suggest
> > we have below properties at dts:
> >
> > - otg-support /* fully otg support */
> > - otg-fsm-support /* fully otg fsm support */
>
> what is the difference between otg-support and otg-fsm-support?
Like I mentioned at above, the hardware finishes HNP/SRP which does not
use otg fsm code (usb-otg-fsm.c), most of legacy otg platforms (musb?)
use this way, for these platforms, only need to set otg-support = 1
For platforms which software finishes HNP/SRP using otg fsm code, need
to set both flags.
For platforms which only do role switch through id pin, do not need to
set both.
>
> > - otg-ver /* eh & otg supplement version */
>
> we can get otg version from the OTG controller. What exactly is the
> otg-ver in dts for?
Since most of otg stuffs are software's, eg, for otg descriptor, we will
only use otg 2.0 descriptor when both CONFIG_USB_OTG20 and otg-ver = 20
are set.
>
> > - adp-support /* board adp support */
> > - srp-support /* board srp support */
> > - hnp-support /* board hnp support */
>
> So if these options are not provided in DTS but the OTG core supports them then
> we keep the respective feature disabled?
Yes.
> Won't this need dts change for existing boards?
Does you know any dts based platform supports hnp/srp?
For chipidea platform, currently, we depends on kernel
configurations (CONFIG_USB_OTG_FSM), but it is incorrect way.
>
> Instead how about having disable flags instead.
> - adp-disable /* board doesn't support adp */
> - srp-disable /* board doesn't support srp */
> - hnp-disable /* board doesn't support hnp */
>
> Now, if the flags are not provided in dts we use the OTG core's flags.
>
How the OTG core's know if it supports these?
> >
> > Currently, if CONFIG_USB_OTG and CONFIG_USB_OTG_FSM are enabled, we will
> > have otg fsm code (usb-otg-fsm.c).
> >
> > if (otg-support & otg-fsm-support)
> > this device has fully otg support, and will follow full otg fsm transitions.
> > else
> > this device is drd, and will follow simple otg fsm transtions.
> >
>
> cheers,
> -roger
>
--
Best Regards,
Peter Chen
On 22/04/15 05:17, Peter Chen wrote:
> On Tue, Apr 21, 2015 at 10:34:01AM +0300, Roger Quadros wrote:
>> On 21/04/15 09:04, Peter Chen wrote:
>>>
>>>>
>>>> On 20/04/15 06:05, Peter Chen wrote:
>>>>> On Tue, Apr 14, 2015 at 01:41:47PM +0300, Roger Quadros wrote:
>>>>>> This is an attempt to centralize OTG/Dual-role functionality in the kernel.
>>>>>> As of now I've got Dual-role functionality working pretty reliably on
>>>>>> dra7-evm. xhci side of things for OTG/DRD use are fixed in
>>>>>> http://thread.gmane.org/gmane.linux.kernel/1923161
>>>>>
>>>>> Hi Roger,
>>>>>
>>>>> Currently, there are two main problems for DRD/OTG framework.
>>>>>
>>>>> - For multi-platform supports, we may define CONFIG_USB_OTG, but the
>>>>> gadget should not add its otg descriptor to its configuration
>>>>> descriptors if it does not support one of otg features (SRP/HNP/ADP).
>>>>> Macpaul Lin's patch set [1] is the right way to do it.
>>>>
>>>> Agreed. That check (whether OTG descriptor can be added and which version
>>>> of it) has to be done at runtime and it must be added only if hardware supports
>>>> OTG _and_ kernel OTG support is enabled.
>>>>
>>>
>>> Ok, let's put this patch set in mainline first, since your patch set may need some
>>> information from it.
>>>
>>>>> - We are lack of framework to handle OTG (DRD) switch, it is great you
>>>>> are designing it. The main problem for this framework is how to handle
>>>>> DRD/OTG FSM unify. My thought is we add two paths for them separate.
>>>>> For easy, I suggest if the platform supports one of otg features, then
>>>>> it goes to fully otg fsm, else it goes to simply otg fsm (like your
>>>>> drd fsm). If you agree with it too, you may not need to add another
>>>> "dr_mode"
>>>>> value.
>>>>
>>>> It would be nice that way but unfortunately it does't work in all cases.
>>>> e.g. What if the SoC itself supports all OTG features but the board is not
>>>> designed for OTG. Or the product designer simply is not interested in full OTG
>>>> support but just dual-role. So we need some flexibility for the device
>>>> tree/platform-data to specify that. This is where a new "dr_mode" == "dual-
>>>> role" is needed.
>>>>
>>>
>>> Since "dr_mode" has been widely used now, if we add a new property for it, we
>>> need to change all drivers.
>>>
>>> Your OTG/DRD framework needs to (partial) use otg fsm, and we will not teach old
>>> driver to use it since there are some driver related stuffs.
>>
>> fair enough. Let's not change dr_mode then and decide based on other parameters.
>>
>>>
>>> SRP/HNP/ADP support can be board level capabilities, and we may consider the
>>> otg device which does not support otg fsm (hardware finishes fsm). So I suggest
>>> we have below properties at dts:
>>>
>>> - otg-support /* fully otg support */
>>> - otg-fsm-support /* fully otg fsm support */
>>
>> what is the difference between otg-support and otg-fsm-support?
>
> Like I mentioned at above, the hardware finishes HNP/SRP which does not
> use otg fsm code (usb-otg-fsm.c), most of legacy otg platforms (musb?)
> use this way, for these platforms, only need to set otg-support = 1
So dr_mode = "otg" _and_ otg-support = 1?
Again wouldn't this involve changes to dts for musb like platforms
supporting full OTG?
Instead we could add a new field saying otg-fsm-type
"otg-hw", "otg-sw", "drd-sw"
If the field is absent it defaults to "otg-hw".
This also means we don't need otg-fsm-support flag.
Now the pseudo-code to decide fsm is
if (dr_mode == "otg" && CONFIG_USB_OTG)
if (otg-fsm-type == "otg-sw") {
if (CONFIG_USB_OTG_FSM)
full otg fsm support via sw
else
error "CONFIG_USB_OTG_FSM" not set
} else if (otg-fsm-type == "drd-sw") {
dual role fsm support
} else {
full otg support via hw
}
if (otg-fsm-type == "otg
else
error "CONFIG_USB_OTG" not set
>
> For platforms which software finishes HNP/SRP using otg fsm code, need
> to set both flags.
>
> For platforms which only do role switch through id pin, do not need to
> set both.
>
OK. I get it now.
>>
>>> - otg-ver /* eh & otg supplement version */
>>
>> we can get otg version from the OTG controller. What exactly is the
>> otg-ver in dts for?
>
> Since most of otg stuffs are software's, eg, for otg descriptor, we will
> only use otg 2.0 descriptor when both CONFIG_USB_OTG20 and otg-ver = 20
> are set.
CONFIG_USB_OTG20 is redundant now. Plus I mentioned in the respective thread
that it is not suitable for booting single image on different platforms.
As of now I can see 2 inputs regarding otg-ver
- One comes from otg-ver DT property or platform data.
- Second that may come from OTG controller registers. e.g. It might support
OTG v3.0 but system designer wants to limit to OTG v2.0 via otg-ver.
Controller driver can decide among the 2 and set the appropriate otg version
in the data structure.
>
>>
>>> - adp-support /* board adp support */
>>> - srp-support /* board srp support */
>>> - hnp-support /* board hnp support */
>>
>> So if these options are not provided in DTS but the OTG core supports them then
>> we keep the respective feature disabled?
>
> Yes.
>
>> Won't this need dts change for existing boards?
>
> Does you know any dts based platform supports hnp/srp?
I'm the wrong person to ask this. Maybe Felipe/Tony can comment.
Irrespective of whether any dts platforms supports hsn/srp or not
we must assume that till date dr_mode = otg implies full otg support
and we cannot change its meaning.
We can add new fields to indicate dual-role mode which is a new feature.
> For chipidea platform, currently, we depends on kernel
> configurations (CONFIG_USB_OTG_FSM), but it is incorrect way.
>
>>
>> Instead how about having disable flags instead.
>> - adp-disable /* board doesn't support adp */
>> - srp-disable /* board doesn't support srp */
>> - hnp-disable /* board doesn't support hnp */
>>
>> Now, if the flags are not provided in dts we use the OTG core's flags.
>>
>
> How the OTG core's know if it supports these?
By OTG core, I meant OTG controller core. At least the dwc3 OTG controller has
register bits to identify if it supports adp/srp/hnp.
But we also need to keep in mind that adp feature can be provided separately even
if the OTG controller core doesn't support it.
cheers,
-roger
>
>>>
>>> Currently, if CONFIG_USB_OTG and CONFIG_USB_OTG_FSM are enabled, we will
>>> have otg fsm code (usb-otg-fsm.c).
>>>
>>> if (otg-support & otg-fsm-support)
>>> this device has fully otg support, and will follow full otg fsm transitions.
>>> else
>>> this device is drd, and will follow simple otg fsm transtions.
>>>
>>
>> cheers,
>> -roger
>>
>
On Wed, Apr 22, 2015 at 10:33:24AM +0300, Roger Quadros wrote:
> On 22/04/15 05:17, Peter Chen wrote:
> > On Tue, Apr 21, 2015 at 10:34:01AM +0300, Roger Quadros wrote:
> >> On 21/04/15 09:04, Peter Chen wrote:
> >>>
> >>>>
> >>>> On 20/04/15 06:05, Peter Chen wrote:
> >>>>> On Tue, Apr 14, 2015 at 01:41:47PM +0300, Roger Quadros wrote:
> >>>>>> This is an attempt to centralize OTG/Dual-role functionality in the kernel.
> >>>>>> As of now I've got Dual-role functionality working pretty reliably on
> >>>>>> dra7-evm. xhci side of things for OTG/DRD use are fixed in
> >>>>>> http://thread.gmane.org/gmane.linux.kernel/1923161
> >>>>>
> >>>>> Hi Roger,
> >>>>>
> >>>>> Currently, there are two main problems for DRD/OTG framework.
> >>>>>
> >>>>> - For multi-platform supports, we may define CONFIG_USB_OTG, but the
> >>>>> gadget should not add its otg descriptor to its configuration
> >>>>> descriptors if it does not support one of otg features (SRP/HNP/ADP).
> >>>>> Macpaul Lin's patch set [1] is the right way to do it.
> >>>>
> >>>> Agreed. That check (whether OTG descriptor can be added and which version
> >>>> of it) has to be done at runtime and it must be added only if hardware supports
> >>>> OTG _and_ kernel OTG support is enabled.
> >>>>
> >>>
> >>> Ok, let's put this patch set in mainline first, since your patch set may need some
> >>> information from it.
> >>>
> >>>>> - We are lack of framework to handle OTG (DRD) switch, it is great you
> >>>>> are designing it. The main problem for this framework is how to handle
> >>>>> DRD/OTG FSM unify. My thought is we add two paths for them separate.
> >>>>> For easy, I suggest if the platform supports one of otg features, then
> >>>>> it goes to fully otg fsm, else it goes to simply otg fsm (like your
> >>>>> drd fsm). If you agree with it too, you may not need to add another
> >>>> "dr_mode"
> >>>>> value.
> >>>>
> >>>> It would be nice that way but unfortunately it does't work in all cases.
> >>>> e.g. What if the SoC itself supports all OTG features but the board is not
> >>>> designed for OTG. Or the product designer simply is not interested in full OTG
> >>>> support but just dual-role. So we need some flexibility for the device
> >>>> tree/platform-data to specify that. This is where a new "dr_mode" == "dual-
> >>>> role" is needed.
> >>>>
> >>>
> >>> Since "dr_mode" has been widely used now, if we add a new property for it, we
> >>> need to change all drivers.
> >>>
> >>> Your OTG/DRD framework needs to (partial) use otg fsm, and we will not teach old
> >>> driver to use it since there are some driver related stuffs.
> >>
> >> fair enough. Let's not change dr_mode then and decide based on other parameters.
> >>
> >>>
> >>> SRP/HNP/ADP support can be board level capabilities, and we may consider the
> >>> otg device which does not support otg fsm (hardware finishes fsm). So I suggest
> >>> we have below properties at dts:
> >>>
> >>> - otg-support /* fully otg support */
> >>> - otg-fsm-support /* fully otg fsm support */
> >>
> >> what is the difference between otg-support and otg-fsm-support?
> >
> > Like I mentioned at above, the hardware finishes HNP/SRP which does not
> > use otg fsm code (usb-otg-fsm.c), most of legacy otg platforms (musb?)
> > use this way, for these platforms, only need to set otg-support = 1
>
> So dr_mode = "otg" _and_ otg-support = 1?
>
Yes
> Again wouldn't this involve changes to dts for musb like platforms
> supporting full OTG?
>
> Instead we could add a new field saying otg-fsm-type
> "otg-hw", "otg-sw", "drd-sw"
>
> If the field is absent it defaults to "otg-hw".
> This also means we don't need otg-fsm-support flag.
>
Yes, that's we doesn't need to change for old platforms, but
we keep our default value as small possibilities, most of current
platforms are only drd platform, it is hard choice.
My original thought was nothing need to add at dts for drd device.
> Now the pseudo-code to decide fsm is
>
> if (dr_mode == "otg" && CONFIG_USB_OTG)
> if (otg-fsm-type == "otg-sw") {
> if (CONFIG_USB_OTG_FSM)
> full otg fsm support via sw
> else
> error "CONFIG_USB_OTG_FSM" not set
> } else if (otg-fsm-type == "drd-sw") {
> dual role fsm support
> } else {
> full otg support via hw
> }
>
> if (otg-fsm-type == "otg
> else
> error "CONFIG_USB_OTG" not set
So we will have a separate drd fsm file, and the CONFIG_USB_OTG
and CONFIG_USB_OTG_FSM are not needed to be defined, right?
>
> >
> > For platforms which software finishes HNP/SRP using otg fsm code, need
> > to set both flags.
> >
> > For platforms which only do role switch through id pin, do not need to
> > set both.
> >
>
> OK. I get it now.
>
> >>
> >>> - otg-ver /* eh & otg supplement version */
> >>
> >> we can get otg version from the OTG controller. What exactly is the
> >> otg-ver in dts for?
> >
> > Since most of otg stuffs are software's, eg, for otg descriptor, we will
> > only use otg 2.0 descriptor when both CONFIG_USB_OTG20 and otg-ver = 20
> > are set.
>
> CONFIG_USB_OTG20 is redundant now. Plus I mentioned in the respective thread
> that it is not suitable for booting single image on different platforms.
>
Ok, agree. Then we need to define two usb otg descriptors
for both 1.3 and 2.0, and we need to have two otg descriptor array
at gadget driver. And the code at gadget driver will like below:
static const struct usb_descriptor_header *otg_desc_13[] = {
(struct usb_descriptor_header *) &(struct usb_otg_descriptor_13){
.bLength = sizeof(struct usb_otg_descriptor_13),
.bDescriptorType = USB_DT_OTG,
/*
* REVISIT SRP-only hardware is possible, although
* it would not be called "OTG" ...
*/
.bmAttributes = USB_OTG_SRP | USB_OTG_HNP,
},
NULL,
};
static const struct usb_descriptor_header *otg_desc_20[] = {
(struct usb_descriptor_header *) &(struct usb_otg_descriptor_20){
.bLength = sizeof(struct usb_otg_descriptor_20),
.bDescriptorType = USB_DT_OTG,
/*
* REVISIT SRP-only hardware is possible, although
* it would not be called "OTG" ...
*/
.bmAttributes = USB_OTG_SRP | USB_OTG_HNP,
.bcdOTG = cpu_to_le16(0x0200),
},
NULL,
};
During bind process:
if (gadget_is_otg_13(c->cdev->gadget))
c->descriptors = otg_desc_13;
else if (gadget_is_otg_20(c->cdev->gadget))
c->descriptors = otg_desc_20;
> As of now I can see 2 inputs regarding otg-ver
> - One comes from otg-ver DT property or platform data.
> - Second that may come from OTG controller registers. e.g. It might support
> OTG v3.0 but system designer wants to limit to OTG v2.0 via otg-ver.
>
> Controller driver can decide among the 2 and set the appropriate otg version
> in the data structure.
>
Yes, it is the same we do for usb mode.
> >
> >>
> >>> - adp-support /* board adp support */
> >>> - srp-support /* board srp support */
> >>> - hnp-support /* board hnp support */
> >>
> >> So if these options are not provided in DTS but the OTG core supports them then
> >> we keep the respective feature disabled?
> >
> > Yes.
> >
> >> Won't this need dts change for existing boards?
> >
> > Does you know any dts based platform supports hnp/srp?
>
> I'm the wrong person to ask this. Maybe Felipe/Tony can comment.
> Irrespective of whether any dts platforms supports hsn/srp or not
> we must assume that till date dr_mode = otg implies full otg support
> and we cannot change its meaning.
>
> We can add new fields to indicate dual-role mode which is a new feature.
>
> > For chipidea platform, currently, we depends on kernel
> > configurations (CONFIG_USB_OTG_FSM), but it is incorrect way.
> >
> >>
> >> Instead how about having disable flags instead.
> >> - adp-disable /* board doesn't support adp */
> >> - srp-disable /* board doesn't support srp */
> >> - hnp-disable /* board doesn't support hnp */
> >>
Like I mentioned above, it depends on which otg features we would like
to give as default features for dual-role device.
For switching the role through the ID pin devices, we doesn't need any otg
features.
For adp/srp/hnp supported devices, we need (partial) otg features, and
the fsm (hardware or software) are needed.
> >> Now, if the flags are not provided in dts we use the OTG core's flags.
> >>
> >
> > How the OTG core's know if it supports these?
>
> By OTG core, I meant OTG controller core. At least the dwc3 OTG controller has
> register bits to identify if it supports adp/srp/hnp.
>
> But we also need to keep in mind that adp feature can be provided separately even
> if the OTG controller core doesn't support it.
>
> cheers,
> -roger
>
> >
> >>>
> >>> Currently, if CONFIG_USB_OTG and CONFIG_USB_OTG_FSM are enabled, we will
> >>> have otg fsm code (usb-otg-fsm.c).
> >>>
> >>> if (otg-support & otg-fsm-support)
> >>> this device has fully otg support, and will follow full otg fsm transitions.
> >>> else
> >>> this device is drd, and will follow simple otg fsm transtions.
> >>>
> >>
> >> cheers,
> >> -roger
> >>
> >
>
--
Best Regards,
Peter Chen
On 22/04/15 12:22, Peter Chen wrote:
> On Wed, Apr 22, 2015 at 10:33:24AM +0300, Roger Quadros wrote:
>> On 22/04/15 05:17, Peter Chen wrote:
>>> On Tue, Apr 21, 2015 at 10:34:01AM +0300, Roger Quadros wrote:
>>>> On 21/04/15 09:04, Peter Chen wrote:
>>>>>
>>>>>>
>>>>>> On 20/04/15 06:05, Peter Chen wrote:
>>>>>>> On Tue, Apr 14, 2015 at 01:41:47PM +0300, Roger Quadros wrote:
>>>>>>>> This is an attempt to centralize OTG/Dual-role functionality in the kernel.
>>>>>>>> As of now I've got Dual-role functionality working pretty reliably on
>>>>>>>> dra7-evm. xhci side of things for OTG/DRD use are fixed in
>>>>>>>> http://thread.gmane.org/gmane.linux.kernel/1923161
>>>>>>>
>>>>>>> Hi Roger,
>>>>>>>
>>>>>>> Currently, there are two main problems for DRD/OTG framework.
>>>>>>>
>>>>>>> - For multi-platform supports, we may define CONFIG_USB_OTG, but the
>>>>>>> gadget should not add its otg descriptor to its configuration
>>>>>>> descriptors if it does not support one of otg features (SRP/HNP/ADP).
>>>>>>> Macpaul Lin's patch set [1] is the right way to do it.
>>>>>>
>>>>>> Agreed. That check (whether OTG descriptor can be added and which version
>>>>>> of it) has to be done at runtime and it must be added only if hardware supports
>>>>>> OTG _and_ kernel OTG support is enabled.
>>>>>>
>>>>>
>>>>> Ok, let's put this patch set in mainline first, since your patch set may need some
>>>>> information from it.
>>>>>
>>>>>>> - We are lack of framework to handle OTG (DRD) switch, it is great you
>>>>>>> are designing it. The main problem for this framework is how to handle
>>>>>>> DRD/OTG FSM unify. My thought is we add two paths for them separate.
>>>>>>> For easy, I suggest if the platform supports one of otg features, then
>>>>>>> it goes to fully otg fsm, else it goes to simply otg fsm (like your
>>>>>>> drd fsm). If you agree with it too, you may not need to add another
>>>>>> "dr_mode"
>>>>>>> value.
>>>>>>
>>>>>> It would be nice that way but unfortunately it does't work in all cases.
>>>>>> e.g. What if the SoC itself supports all OTG features but the board is not
>>>>>> designed for OTG. Or the product designer simply is not interested in full OTG
>>>>>> support but just dual-role. So we need some flexibility for the device
>>>>>> tree/platform-data to specify that. This is where a new "dr_mode" == "dual-
>>>>>> role" is needed.
>>>>>>
>>>>>
>>>>> Since "dr_mode" has been widely used now, if we add a new property for it, we
>>>>> need to change all drivers.
>>>>>
>>>>> Your OTG/DRD framework needs to (partial) use otg fsm, and we will not teach old
>>>>> driver to use it since there are some driver related stuffs.
>>>>
>>>> fair enough. Let's not change dr_mode then and decide based on other parameters.
>>>>
>>>>>
>>>>> SRP/HNP/ADP support can be board level capabilities, and we may consider the
>>>>> otg device which does not support otg fsm (hardware finishes fsm). So I suggest
>>>>> we have below properties at dts:
>>>>>
>>>>> - otg-support /* fully otg support */
>>>>> - otg-fsm-support /* fully otg fsm support */
>>>>
>>>> what is the difference between otg-support and otg-fsm-support?
>>>
>>> Like I mentioned at above, the hardware finishes HNP/SRP which does not
>>> use otg fsm code (usb-otg-fsm.c), most of legacy otg platforms (musb?)
>>> use this way, for these platforms, only need to set otg-support = 1
>>
>> So dr_mode = "otg" _and_ otg-support = 1?
>>
>
> Yes
>
>> Again wouldn't this involve changes to dts for musb like platforms
>> supporting full OTG?
>>
>> Instead we could add a new field saying otg-fsm-type
>> "otg-hw", "otg-sw", "drd-sw"
>>
>> If the field is absent it defaults to "otg-hw".
>> This also means we don't need otg-fsm-support flag.
>>
>
> Yes, that's we doesn't need to change for old platforms, but
> we keep our default value as small possibilities, most of current
> platforms are only drd platform, it is hard choice.
>
> My original thought was nothing need to add at dts for drd device.
>
>> Now the pseudo-code to decide fsm is
>>
>> if (dr_mode == "otg" && CONFIG_USB_OTG)
>> if (otg-fsm-type == "otg-sw") {
>> if (CONFIG_USB_OTG_FSM)
>> full otg fsm support via sw
>> else
>> error "CONFIG_USB_OTG_FSM" not set
>> } else if (otg-fsm-type == "drd-sw") {
>> dual role fsm support
>> } else {
>> full otg support via hw
>> }
>>
>> if (otg-fsm-type == "otg
>> else
>> error "CONFIG_USB_OTG" not set
>
> So we will have a separate drd fsm file, and the CONFIG_USB_OTG
> and CONFIG_USB_OTG_FSM are not needed to be defined, right?
>
for drd case CONFIG_USB_OTG_FSM is definitely not needed.
I'm not sure if we can operate dual-role without CONFIG_USB_OTG.
I was thinking of combining the OTG core functionality (drivers/usb/common/usb-otg.c)
with CONFIG_USB_OTG.
>>
>>>
>>> For platforms which software finishes HNP/SRP using otg fsm code, need
>>> to set both flags.
>>>
>>> For platforms which only do role switch through id pin, do not need to
>>> set both.
>>>
>>
>> OK. I get it now.
>>
>>>>
>>>>> - otg-ver /* eh & otg supplement version */
>>>>
>>>> we can get otg version from the OTG controller. What exactly is the
>>>> otg-ver in dts for?
>>>
>>> Since most of otg stuffs are software's, eg, for otg descriptor, we will
>>> only use otg 2.0 descriptor when both CONFIG_USB_OTG20 and otg-ver = 20
>>> are set.
>>
>> CONFIG_USB_OTG20 is redundant now. Plus I mentioned in the respective thread
>> that it is not suitable for booting single image on different platforms.
>>
>
> Ok, agree. Then we need to define two usb otg descriptors
> for both 1.3 and 2.0, and we need to have two otg descriptor array
> at gadget driver. And the code at gadget driver will like below:
>
> static const struct usb_descriptor_header *otg_desc_13[] = {
> (struct usb_descriptor_header *) &(struct usb_otg_descriptor_13){
> .bLength = sizeof(struct usb_otg_descriptor_13),
> .bDescriptorType = USB_DT_OTG,
>
> /*
> * REVISIT SRP-only hardware is possible, although
> * it would not be called "OTG" ...
> */
> .bmAttributes = USB_OTG_SRP | USB_OTG_HNP,
> },
> NULL,
> };
>
> static const struct usb_descriptor_header *otg_desc_20[] = {
> (struct usb_descriptor_header *) &(struct usb_otg_descriptor_20){
> .bLength = sizeof(struct usb_otg_descriptor_20),
> .bDescriptorType = USB_DT_OTG,
>
> /*
> * REVISIT SRP-only hardware is possible, although
> * it would not be called "OTG" ...
> */
> .bmAttributes = USB_OTG_SRP | USB_OTG_HNP,
> .bcdOTG = cpu_to_le16(0x0200),
> },
> NULL,
> };
instead of hardcoding bmAttributes field, it must be obtained from the
appropriate data structure that was set by the controller driver
by reading DT and OTG hardware info.
>
> During bind process:
>
> if (gadget_is_otg_13(c->cdev->gadget))
> c->descriptors = otg_desc_13;
> else if (gadget_is_otg_20(c->cdev->gadget))
> c->descriptors = otg_desc_20;
Probably a helper utitily can do the necessary checks and build the
otg descriptor for us.
usb_otg_get_descriptor(c->descriptors);
e.g. for OTG3.0 we have a new flag USB_OTG_RSP, and this helper
can be upgraded in the future.
>
>> As of now I can see 2 inputs regarding otg-ver
>> - One comes from otg-ver DT property or platform data.
>> - Second that may come from OTG controller registers. e.g. It might support
>> OTG v3.0 but system designer wants to limit to OTG v2.0 via otg-ver.
>>
>> Controller driver can decide among the 2 and set the appropriate otg version
>> in the data structure.
>>
>
> Yes, it is the same we do for usb mode.
OK.
cheers,
-roger
>
>>>
>>>>
>>>>> - adp-support /* board adp support */
>>>>> - srp-support /* board srp support */
>>>>> - hnp-support /* board hnp support */
>>>>
>>>> So if these options are not provided in DTS but the OTG core supports them then
>>>> we keep the respective feature disabled?
>>>
>>> Yes.
>>>
>>>> Won't this need dts change for existing boards?
>>>
>>> Does you know any dts based platform supports hnp/srp?
>>
>> I'm the wrong person to ask this. Maybe Felipe/Tony can comment.
>> Irrespective of whether any dts platforms supports hsn/srp or not
>> we must assume that till date dr_mode = otg implies full otg support
>> and we cannot change its meaning.
>>
>> We can add new fields to indicate dual-role mode which is a new feature.
>
>>
>>> For chipidea platform, currently, we depends on kernel
>>> configurations (CONFIG_USB_OTG_FSM), but it is incorrect way.
>>>
>>>>
>>>> Instead how about having disable flags instead.
>>>> - adp-disable /* board doesn't support adp */
>>>> - srp-disable /* board doesn't support srp */
>>>> - hnp-disable /* board doesn't support hnp */
>>>>
>
> Like I mentioned above, it depends on which otg features we would like
> to give as default features for dual-role device.
>
> For switching the role through the ID pin devices, we doesn't need any otg
> features.
> For adp/srp/hnp supported devices, we need (partial) otg features, and
> the fsm (hardware or software) are needed.
>
>>>> Now, if the flags are not provided in dts we use the OTG core's flags.
>>>>
>>>
>>> How the OTG core's know if it supports these?
>>
>> By OTG core, I meant OTG controller core. At least the dwc3 OTG controller has
>> register bits to identify if it supports adp/srp/hnp.
>>
>> But we also need to keep in mind that adp feature can be provided separately even
>> if the OTG controller core doesn't support it.
>>
>> cheers,
>> -roger
>>
>>>
>>>>>
>>>>> Currently, if CONFIG_USB_OTG and CONFIG_USB_OTG_FSM are enabled, we will
>>>>> have otg fsm code (usb-otg-fsm.c).
>>>>>
>>>>> if (otg-support & otg-fsm-support)
>>>>> this device has fully otg support, and will follow full otg fsm transitions.
>>>>> else
>>>>> this device is drd, and will follow simple otg fsm transtions.
>>>>>
>>>>
>>>> cheers,
>>>> -roger
>>>>
>>>
>>
>
On Wed, Apr 22, 2015 at 03:42:32PM +0300, Roger Quadros wrote:
> > So we will have a separate drd fsm file, and the CONFIG_USB_OTG
> > and CONFIG_USB_OTG_FSM are not needed to be defined, right?
> >
>
> for drd case CONFIG_USB_OTG_FSM is definitely not needed.
> I'm not sure if we can operate dual-role without CONFIG_USB_OTG.
> I was thinking of combining the OTG core functionality (drivers/usb/common/usb-otg.c)
> with CONFIG_USB_OTG.
>
Ok, let's choose CONFIG_USB_OTG for both drd and usb fsm case.
And we need to patch for hcd that only hnp supported hcd needs
to request otg descriptor, etc.
For yesterday's back-compatible old otg device, we can add otg
features to these drivers, the current behavior for these drivers
is: if CONFIG_USB_OTG is defined, it is an otg device, we can just
keep the behavior unchanging. If these drivers need to use OTG framework
in future, it needs to update its platform data or dts.
So, I prefer:
- For switching the role through the ID pin devices, we doesn't need any otg
features, so no otg dts properties are needed.(expect dr_mode = "otg")
- For adp/srp/hnp supported devices, we need (partial) otg features, and
the fsm (hardware or software) are needed, we need some otg dts
properties we discussed before.
> >
> > static const struct usb_descriptor_header *otg_desc_20[] = {
> > (struct usb_descriptor_header *) &(struct usb_otg_descriptor_20){
> > .bLength = sizeof(struct usb_otg_descriptor_20),
> > .bDescriptorType = USB_DT_OTG,
> >
> > /*
> > * REVISIT SRP-only hardware is possible, although
> > * it would not be called "OTG" ...
> > */
> > .bmAttributes = USB_OTG_SRP | USB_OTG_HNP,
> > .bcdOTG = cpu_to_le16(0x0200),
> > },
> > NULL,
> > };
>
> instead of hardcoding bmAttributes field, it must be obtained from the
> appropriate data structure that was set by the controller driver
> by reading DT and OTG hardware info.
>
> >
> > During bind process:
> >
> > if (gadget_is_otg_13(c->cdev->gadget))
> > c->descriptors = otg_desc_13;
> > else if (gadget_is_otg_20(c->cdev->gadget))
> > c->descriptors = otg_desc_20;
>
> Probably a helper utitily can do the necessary checks and build the
> otg descriptor for us.
> usb_otg_get_descriptor(c->descriptors);
>
> e.g. for OTG3.0 we have a new flag USB_OTG_RSP, and this helper
> can be upgraded in the future.
ok, it is the implementation detail.
--
Best Regards,
Peter Chen
On 23/04/15 04:52, Peter Chen wrote:
> On Wed, Apr 22, 2015 at 03:42:32PM +0300, Roger Quadros wrote:
>>> So we will have a separate drd fsm file, and the CONFIG_USB_OTG
>>> and CONFIG_USB_OTG_FSM are not needed to be defined, right?
>>>
>>
>> for drd case CONFIG_USB_OTG_FSM is definitely not needed.
>> I'm not sure if we can operate dual-role without CONFIG_USB_OTG.
>> I was thinking of combining the OTG core functionality (drivers/usb/common/usb-otg.c)
>> with CONFIG_USB_OTG.
>>
>
> Ok, let's choose CONFIG_USB_OTG for both drd and usb fsm case.
> And we need to patch for hcd that only hnp supported hcd needs
> to request otg descriptor, etc.
Agreed. It makes things much simpler.
>
> For yesterday's back-compatible old otg device, we can add otg
> features to these drivers, the current behavior for these drivers
> is: if CONFIG_USB_OTG is defined, it is an otg device, we can just
> keep the behavior unchanging. If these drivers need to use OTG framework
> in future, it needs to update its platform data or dts.
Right.
>
> So, I prefer:
>
> - For switching the role through the ID pin devices, we doesn't need any otg
> features, so no otg dts properties are needed.(expect dr_mode = "otg")
> - For adp/srp/hnp supported devices, we need (partial) otg features, and
> the fsm (hardware or software) are needed, we need some otg dts
> properties we discussed before.
Agreed.
I will try to incorporate these points in v3 of this series.
cheers,
-roger
>
>
>>>
>>> static const struct usb_descriptor_header *otg_desc_20[] = {
>>> (struct usb_descriptor_header *) &(struct usb_otg_descriptor_20){
>>> .bLength = sizeof(struct usb_otg_descriptor_20),
>>> .bDescriptorType = USB_DT_OTG,
>>>
>>> /*
>>> * REVISIT SRP-only hardware is possible, although
>>> * it would not be called "OTG" ...
>>> */
>>> .bmAttributes = USB_OTG_SRP | USB_OTG_HNP,
>>> .bcdOTG = cpu_to_le16(0x0200),
>>> },
>>> NULL,
>>> };
>>
>> instead of hardcoding bmAttributes field, it must be obtained from the
>> appropriate data structure that was set by the controller driver
>> by reading DT and OTG hardware info.
>>
>>>
>>> During bind process:
>>>
>>> if (gadget_is_otg_13(c->cdev->gadget))
>>> c->descriptors = otg_desc_13;
>>> else if (gadget_is_otg_20(c->cdev->gadget))
>>> c->descriptors = otg_desc_20;
>>
>> Probably a helper utitily can do the necessary checks and build the
>> otg descriptor for us.
>> usb_otg_get_descriptor(c->descriptors);
>>
>> e.g. for OTG3.0 we have a new flag USB_OTG_RSP, and this helper
>> can be upgraded in the future.
>
> ok, it is the implementation detail.
>