Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > linux.kernel > #1419385 > unrolled thread
| Started by | Roger Quadros <rogerq@ti.com> |
|---|---|
| First post | 2016-06-10 15:10 +0200 |
| Last post | 2016-06-10 15:20 +0200 |
| Articles | 5 — 1 participant |
Back to article view | Back to linux.kernel
[PATCH v10 00/14] USB OTG/dual-role framework Roger Quadros <rogerq@ti.com> - 2016-06-10 15:10 +0200
[PATCH v10 08/14] usb: otg: add OTG/dual-role core Roger Quadros <rogerq@ti.com> - 2016-06-10 15:10 +0200
[PATCH v10 03/14] usb: hcd.h: Add OTG to HCD interface Roger Quadros <rogerq@ti.com> - 2016-06-10 15:20 +0200
[PATCH v10 10/14] usb: otg: add hcd companion support Roger Quadros <rogerq@ti.com> - 2016-06-10 15:20 +0200
[PATCH v10 09/14] usb: of: add an API to get OTG device from USB controller node Roger Quadros <rogerq@ti.com> - 2016-06-10 15:20 +0200
| From | Roger Quadros <rogerq@ti.com> |
|---|---|
| Date | 2016-06-10 15:10 +0200 |
| Subject | [PATCH v10 00/14] USB OTG/dual-role framework |
| Message-ID | <rIumd-4YI-3@gated-at.bofh.it> |
Hi,
This series centralizes OTG/Dual-role functionality in the kernel.
As of now I've got Dual-role functionality working pretty reliably on
dra7-evm and am437x-gp-evm.
DWC3 controller and TI platform related patches will be sent separately.
Series is based on v4.7-rc1 + balbi/usb.git testing/next
commit 4a2786c10462df650965785462ca82c185164d98.
Why?:
----
Currently there is no central location where OTG/dual-role functionality is
implemented in the Linux USB stack and every USB controller driver is
doing their own thing for OTG/dual-role. We can benefit from code-reuse
and simplicity by adding the OTG/dual-role core driver.
Newer OTG cores support standard host interface (e.g. xHCI) so
host and gadget functionality are no longer closely knit like older
cores. There needs to be a way to co-ordinate the operation of the
host and gadget controllers in dual-role mode. i.e. to stop and start them
from a central location. This central location should be the
USB OTG/dual-role core.
Host and gadget controllers might be sharing resources and can't
be always running. One has to be stopped for the other to run.
This couldn't be done till now but can be done from the OTG core.
What?:
-----
The OTG/dual-role core consists of a set of APIs that allow
registration of OTG controller device and OTG capable host and gadget
controllers.
- The OTG controller driver can provide the OTG capabilities and the
Finite State Machine work function via 'struct usb_otg_config'
at the time of registration i.e. usb_otg_register();
struct usb_otg *usb_otg_register(struct device *dev,
struct usb_otg_config *config);
int usb_otg_unregister(struct device *dev);
/**
* struct usb_otg_config - otg controller configuration
* @caps: otg capabilities of the controller
* @ops: otg fsm operations
* @otg_work: optional custom otg state machine work function
*/
struct usb_otg_config {
struct usb_otg_caps *otg_caps;
struct otg_fsm_ops *fsm_ops;
void (*otg_work)(struct work_struct *work);
};
The dual-role state machine is built-into the OTG core so nothing
special needs to be provided if only dual-role functionality is desired.
The low level OTG controller driver ops are povided via
'struct otg_fsm_ops *fsm_ops' in the 'struct usb_otg_config'.
After registration, the OTG core waits for host, gadget controller
and the gadget function driver to be registered. Once all resources are
available it instantiates the Finite State Machine (FSM).
The host/gadget controllers are started/stopped according to the FSM.
- Host and gadget controllers that are a part of OTG/dual-role port must
use the OTG core provided APIs to add/remove the host/gadget.
i.e. hosts must use usb_otg_add_hcd() usb_otg_remove_hcd(),,
gadgets must use usb_otg_add_gadget_udc() usb_del_gadget_udc().
This ensures that the host and gadget controllers are not started till
the state machine is ready and the right bus conditions are met.
It also allows the host and gadget controllers to provide the OTG
controller device to link them together. For Device tree boots
the related OTG controller is automatically picked up via the
'otg-controller' property in the Host/Gadget controller nodes.
int usb_otg_add_hcd(struct usb_hcd *hcd,
unsigned int irqnum, unsigned long irqflags,
struct device *otg_dev);
void usb_otg_remove_hcd(struct usb_hcd *hcd);
int usb_otg_add_gadget_udc(struct device *parent,
struct usb_gadget *gadget,
struct device *otg_dev);
usb_del_gadget_udc() must be used for removal.
- During the lifetime of the FSM, the OTG controller driver can provide
inputs event changes using usb_otg_sync_inputs(). The OTG core will
then schedule the FSM work function (or internal dual-role state machine)
to update the FSM state. The FSM then calls the OTG controller
operations (fsm_ops) as necessary.
void usb_otg_sync_inputs(struct usb_otg *otg);
- The following 2 functions are provided as helpers for use by the
OTG controller driver to start/stop the host/gadget controllers.
int usb_otg_start_host(struct usb_otg *otg, int on);
int usb_otg_start_gadget(struct usb_otg *otg, int on);
- The following function is provided for use by the USB host stack
to sync OTG related events to the OTG state machine.
e.g. change in host_bus->b_hnp_enable, gadget->b_hnp_enable
int usb_otg_kick_fsm(struct device *otg_device);
- The following function is provided for use by the USB gadget stack
to notify OTG/DRD about gadget function driver being ready/not-ready
int usb_otg_gadget_ready(struct usb_gadget *gadget, bool ready)
Changelog:
---------
v10:
- added missing mutex_unlock in failure path in usb_otg_unregister()
- removed unnecessary hrtimer.h and ktime.h in linux/usb/otg.h
- comment fixes: urb->URB.
- fix commit log: say dev_vdbg() instead of dev_dbg().
- Update comments so that HCD means Host Controller Driver.
- rebased on Felipe's balbi/usb.git /testing/next
- include <linux/usb/of.h> in usb-otg.c
- in usb_otg_start_host() check if hcd_ops->add() failed and return
error code if it did.
- in usb_otg_start/stop_gadget() check if hcd_ops->add() failed and return
error code if it did.
- follow kernel-doc style in usb-otg.c
v9:
- In the DT bindings, clearly indicate which properties are for OTG controller
and which ones are for host/device controllers
- Removed host/gadget wait list from otg core. Use defer probing if host/gadget
registers before dual-role/otg controller.
- move gadget registering to otg core from udc_bind_to_driver()/usb_gadget_remove_driver()
to usb_add_gadget_udc_release()/usb_del_gadget_udc(). This means that we need
an additional mechanism to know when the gadget function driver registers.
So we add usb_otg_gadget_ready() API to allow gadget to notify otg core about
ready/not-ready function driver.
- update otg->caps based on capabilities provided by the controller and the
device tree overrides.
- in usb_drd_work() we must keep calling the drd_statemachine() as long as
there is a state change.
- updated kconfig so that usbotg can be a module when both host and gadget
are modules.
- rebased on v4.7-rc1
v8:
- split out start/stop gadget and connect/disconnect operations.
- make CONFIG_OTG dpend on CONFIG_USB_GADGET as well apart from CONFIG_USB
- use create_freezable_workqueue() for OTG work as per Peter's suggestion.
- remove usb-otg.h as we're not initializing any OTG timers.
- don't include unnecessary headers in usb-otg.c (i.e. hrtimer.h & ktime.h)
v7:
- added dual-role support for host controllers requiring a companion
controller. e.g. EHCI + OHCI.
- added of_usb_get_otg() to get the OTG controller device
from the USB controller's device node.
- addressed review comments.
v6:
- added otg specific APIs for host/gadget registration. behaviour of
original host/gadget API remains unchanged. Platform devices can now
pass the otg device explicitly while registering host/gadget.
- moved hcd specific operations from struct otg_fsm to struct hcd_ops.
- made struct usb_otg mandatory for all otg related APIs.
- allow otg controller to provide it's own otg_work function so that
it can implement it's own state machine.
- removed otg fsm and timers from usb-otg.c. Only dual-role state machine
is implemented.
- vbus is controlled in the dual-role state machine.
- PM runtime is used around drd_statemachine().
- added otg_dev to xhci platform data to allow platform code to specify
the otg controller tied to the xhci host controller.
v5: Internal version. Not sent to mailing list
v4:
- Added DT support for tying otg-controller to host and gadget
controllers. For DT we no longer have the constraint that
OTG controller needs to be parent of host and gadget. They can be
tied together using the "otg-controller" property.
- Relax the requirement for DT case that otg controller must register
before host/gadget. We maintain a wait list of host/gadget devices
waiting on the otg controller.
- Use a single struct usb_otg for otg data.
- Don't override host/gadget start/stop APIs. Let the controller
drivers do what they want as they know best. Helper API is provided
for controller start/stop that controller driver can use.
- Introduce struct usb_otg_config to pass the otg capabilities,
otg ops and otg timer timeouts during otg controller registration.
- rebased on Greg's usb.git/usb-next
v3:
- all otg related definations now in otg.h
- single kernel config USB_OTG to enable OTG core and FSM.
- resolved symbol dependency issues.
- use dev_vdbg instead of VDBG() in usb-otg-fsm.c
- rebased on v4.2-rc1
v2:
- Use add/remove_hcd() instead of start/stop_hcd() to enable/disable
the host controller
- added dual-role-device (DRD) state machine which is a much simpler
mode of operation when compared to OTG. Here we don't support fancy
OTG features like HNP, SRP, on the fly role-swap. The mode of operation
is determined based on ID pin (cable type) and the role doesn't change
till the cable type changes.
--
cheers,
-roger
Roger Quadros (13):
usb: hcd: Initialize hcd->flags to 0
usb: otg-fsm: Prevent build warning "VDBG" redefined
usb: hcd.h: Add OTG to HCD interface
usb: otg-fsm: use usb_otg wherever possible
usb: otg-fsm: move host controller operations into usb_otg->hcd_ops
usb: gadget.h: Add OTG to gadget interface
usb: otg: get rid of CONFIG_USB_OTG_FSM in favour of CONFIG_USB_OTG
usb: otg: add OTG/dual-role core
usb: of: add an API to get OTG device from USB controller node
usb: otg: use dev_vdbg() instead of VDBG()
usb: hcd: Adapt to OTG core
usb: gadget: udc: adapt to OTG core
usb: host: xhci-plat: Add otg device to platform data
Yoshihiro Shimoda (1):
usb: otg: add hcd companion support
Documentation/devicetree/bindings/usb/generic.txt | 6 +
Documentation/usb/chipidea.txt | 2 +-
drivers/usb/Kconfig | 18 +
drivers/usb/Makefile | 1 +
drivers/usb/chipidea/Makefile | 2 +-
drivers/usb/chipidea/ci.h | 3 +-
drivers/usb/chipidea/core.c | 14 +-
drivers/usb/chipidea/debug.c | 2 +-
drivers/usb/chipidea/otg_fsm.c | 176 +++--
drivers/usb/chipidea/otg_fsm.h | 2 +-
drivers/usb/chipidea/udc.c | 17 +-
drivers/usb/common/Makefile | 5 +-
drivers/usb/common/common.c | 27 +
drivers/usb/common/usb-otg-fsm.c | 203 ++---
drivers/usb/common/usb-otg.c | 919 ++++++++++++++++++++++
drivers/usb/core/Kconfig | 22 -
drivers/usb/core/hcd.c | 56 ++
drivers/usb/gadget/Kconfig | 1 +
drivers/usb/gadget/udc/core.c | 202 ++++-
drivers/usb/host/xhci-plat.c | 35 +-
drivers/usb/phy/Kconfig | 2 +-
drivers/usb/phy/phy-fsl-usb.c | 155 ++--
drivers/usb/phy/phy-fsl-usb.h | 3 +-
include/linux/usb/gadget.h | 22 +
include/linux/usb/hcd.h | 29 +
include/linux/usb/of.h | 9 +
include/linux/usb/otg-fsm.h | 154 +---
include/linux/usb/otg.h | 290 ++++++-
include/linux/usb/xhci_pdriver.h | 3 +
29 files changed, 1918 insertions(+), 462 deletions(-)
create mode 100644 drivers/usb/common/usb-otg.c
--
2.7.4
[toc] | [next] | [standalone]
| From | Roger Quadros <rogerq@ti.com> |
|---|---|
| Date | 2016-06-10 15:10 +0200 |
| Subject | [PATCH v10 08/14] usb: otg: add OTG/dual-role core |
| Message-ID | <rIumf-4YI-45@gated-at.bofh.it> |
| In reply to | #1419385 |
It provides APIs for the following tasks
- Registering an OTG/dual-role capable controller
- Registering Host and Gadget controllers to OTG core
- Providing inputs to and kicking the OTG state machine
Provide a dual-role device (DRD) state machine.
DRD mode is a reduced functionality OTG mode. In this mode
we don't support SRP, HNP and dynamic role-swap.
In DRD operation, the controller mode (Host or Peripheral)
is decided based on the ID pin status. Once a cable plug (Type-A
or Type-B) is attached the controller selects the state
and doesn't change till the cable in unplugged and a different
cable type is inserted.
As we don't need most of the complex OTG states and OTG timers
we implement a lean DRD state machine in usb-otg.c.
The DRD state machine is only interested in 2 hardware inputs
'id' and 'b_sess_vld'.
Signed-off-by: Roger Quadros <rogerq@ti.com>
---
drivers/usb/Kconfig | 18 +
drivers/usb/Makefile | 1 +
drivers/usb/common/Makefile | 6 +-
drivers/usb/common/usb-otg.c | 906 +++++++++++++++++++++++++++++++++++++++++++
drivers/usb/core/Kconfig | 14 -
drivers/usb/gadget/Kconfig | 1 +
include/linux/usb/gadget.h | 2 +
include/linux/usb/hcd.h | 1 +
include/linux/usb/otg-fsm.h | 7 +
include/linux/usb/otg.h | 180 ++++++++-
10 files changed, 1106 insertions(+), 30 deletions(-)
create mode 100644 drivers/usb/common/usb-otg.c
diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index 8689dcb..ed596ec 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -32,6 +32,23 @@ if USB_SUPPORT
config USB_COMMON
tristate
+config USB_OTG_CORE
+ tristate
+
+config USB_OTG
+ bool "OTG/Dual-role support"
+ depends on PM && USB && USB_GADGET
+ default n
+ ---help---
+ The most notable feature of USB OTG is support for a
+ "Dual-Role" device, which can act as either a device
+ or a host. The initial role is decided by the type of
+ plug inserted and can be changed later when two dual
+ role devices talk to each other.
+
+ Select this only if your board has Mini-AB/Micro-AB
+ connector.
+
config USB_ARCH_HAS_HCD
def_bool y
@@ -40,6 +57,7 @@ config USB
tristate "Support for Host-side USB"
depends on USB_ARCH_HAS_HCD
select USB_COMMON
+ select USB_OTG_CORE
select NLS # for UTF-8 strings
---help---
Universal Serial Bus (USB) is a specification for a serial bus
diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
index dca7856..03f7204 100644
--- a/drivers/usb/Makefile
+++ b/drivers/usb/Makefile
@@ -59,5 +59,6 @@ obj-$(CONFIG_USB_RENESAS_USBHS) += renesas_usbhs/
obj-$(CONFIG_USB_GADGET) += gadget/
obj-$(CONFIG_USB_COMMON) += common/
+obj-$(CONFIG_USB_OTG_CORE) += common/
obj-$(CONFIG_USBIP_CORE) += usbip/
diff --git a/drivers/usb/common/Makefile b/drivers/usb/common/Makefile
index f8f2c88..5122b3f 100644
--- a/drivers/usb/common/Makefile
+++ b/drivers/usb/common/Makefile
@@ -7,5 +7,7 @@ usb-common-y += common.o
usb-common-$(CONFIG_USB_LED_TRIG) += led.o
obj-$(CONFIG_USB_ULPI_BUS) += ulpi.o
-usbotg-y := usb-otg-fsm.o
-obj-$(CONFIG_USB_OTG) += usbotg.o
+ifeq ($(CONFIG_USB_OTG),y)
+usbotg-y := usb-otg.o usb-otg-fsm.o
+obj-$(CONFIG_USB_OTG_CORE) += usbotg.o
+endif
diff --git a/drivers/usb/common/usb-otg.c b/drivers/usb/common/usb-otg.c
new file mode 100644
index 0000000..4185954
--- /dev/null
+++ b/drivers/usb/common/usb-otg.c
@@ -0,0 +1,906 @@
+/**
+ * drivers/usb/common/usb-otg.c - USB OTG core
+ *
+ * Copyright (C) 2016 Texas Instruments Incorporated - http://www.ti.com
+ * Author: Roger Quadros <rogerq@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/usb/of.h>
+#include <linux/usb/otg.h>
+#include <linux/usb/gadget.h>
+#include <linux/workqueue.h>
+
+/* OTG device list */
+LIST_HEAD(otg_list);
+static DEFINE_MUTEX(otg_list_mutex);
+
+static int usb_otg_hcd_is_primary_hcd(struct usb_hcd *hcd)
+{
+ if (!hcd->primary_hcd)
+ return 1;
+ return hcd == hcd->primary_hcd;
+}
+
+/**
+ * usb_otg_get_data() - get usb_otg data structa
+ * @otg_dev: OTG controller device
+ *
+ * Check if the OTG device is in our OTG list and return
+ * usb_otg data, else NULL.
+ *
+ * otg_list_mutex must be held.
+ *
+ * Return: usb_otg data on success, NULL otherwise.
+ */
+static struct usb_otg *usb_otg_get_data(struct device *otg_dev)
+{
+ struct usb_otg *otg;
+
+ if (!otg_dev)
+ return NULL;
+
+ list_for_each_entry(otg, &otg_list, list) {
+ if (otg->dev == otg_dev)
+ return otg;
+ }
+
+ return NULL;
+}
+
+/**
+ * usb_otg_start_host() - start/stop the host controller
+ * @otg: usb_otg instance
+ * @on: true to start, false to stop
+ *
+ * Start/stop the USB host controller. This function is meant
+ * for use by the OTG controller driver.
+ *
+ * Return: 0 on success, error value otherwise.
+ */
+int usb_otg_start_host(struct usb_otg *otg, int on)
+{
+ struct otg_hcd_ops *hcd_ops = otg->hcd_ops;
+ int ret;
+
+ dev_dbg(otg->dev, "otg: %s %d\n", __func__, on);
+ if (!otg->host) {
+ WARN_ONCE(1, "otg: fsm running without host\n");
+ return 0;
+ }
+
+ if (on) {
+ if (otg->flags & OTG_FLAG_HOST_RUNNING)
+ return 0;
+
+ /* start host */
+ ret = hcd_ops->add(otg->primary_hcd.hcd,
+ otg->primary_hcd.irqnum,
+ otg->primary_hcd.irqflags);
+ if (ret) {
+ dev_err(otg->dev, "otg: host add failed %d\n", ret);
+ return ret;
+ }
+
+ if (otg->shared_hcd.hcd) {
+ ret = hcd_ops->add(otg->shared_hcd.hcd,
+ otg->shared_hcd.irqnum,
+ otg->shared_hcd.irqflags);
+ if (ret) {
+ dev_err(otg->dev, "otg: shared host add failed %d\n",
+ ret);
+ hcd_ops->remove(otg->primary_hcd.hcd);
+ return ret;
+ }
+ }
+ otg->flags |= OTG_FLAG_HOST_RUNNING;
+ } else {
+ if (!(otg->flags & OTG_FLAG_HOST_RUNNING))
+ return 0;
+
+ otg->flags &= ~OTG_FLAG_HOST_RUNNING;
+
+ /* stop host */
+ if (otg->shared_hcd.hcd)
+ hcd_ops->remove(otg->shared_hcd.hcd);
+
+ hcd_ops->remove(otg->primary_hcd.hcd);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usb_otg_start_host);
+
+/**
+ * usb_otg_start_gadget() - start/stop the gadget controller
+ * @otg: usb_otg instance
+ * @on: true to start, false to stop
+ *
+ * Start/stop the USB gadget controller. This function is meant
+ * for use by the OTG controller driver.
+ *
+ * Return: 0 on success, error value otherwise.
+ */
+int usb_otg_start_gadget(struct usb_otg *otg, int on)
+{
+ struct usb_gadget *gadget = otg->gadget;
+ int ret;
+
+ dev_dbg(otg->dev, "otg: %s %d\n", __func__, on);
+ if (!gadget) {
+ WARN_ONCE(1, "otg: fsm running without gadget\n");
+ return 0;
+ }
+
+ if (on) {
+ if (otg->flags & OTG_FLAG_GADGET_RUNNING)
+ return 0;
+
+ ret = otg->gadget_ops->start(otg->gadget);
+ if (ret) {
+ dev_err(otg->dev, "otg: gadget start failed: %d\n",
+ ret);
+ return ret;
+ }
+
+ otg->flags |= OTG_FLAG_GADGET_RUNNING;
+ } else {
+ if (!(otg->flags & OTG_FLAG_GADGET_RUNNING))
+ return 0;
+
+ ret = otg->gadget_ops->stop(otg->gadget);
+ if (ret) {
+ dev_err(otg->dev, "otg: gadget stop failed: %d\n",
+ ret);
+ return ret;
+ }
+ otg->flags &= ~OTG_FLAG_GADGET_RUNNING;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usb_otg_start_gadget);
+
+/**
+ * drd_set_protocol() - Set USB protocol if possible
+ * @fsm: DRD FSM instance
+ * @protocol: USB protocol to set the state machine to
+ *
+ * Sets the OTG FSM protocol to @protocol if it changed.
+ * fsm->lock must be held.
+ *
+ * Return: 0 on success, error value otherwise.
+ */
+static int drd_set_protocol(struct otg_fsm *fsm, int protocol)
+{
+ struct usb_otg *otg = container_of(fsm, struct usb_otg, fsm);
+ int ret = 0;
+
+ if (fsm->protocol != protocol) {
+ dev_dbg(otg->dev, "otg: changing role fsm->protocol= %d; new protocol= %d\n",
+ fsm->protocol, protocol);
+ /* stop old protocol */
+ if (fsm->protocol == PROTO_HOST) {
+ ret = otg_start_host(otg, 0);
+ } else if (fsm->protocol == PROTO_GADGET) {
+ otg->gadget_ops->connect_control(otg->gadget, false);
+ ret = otg_start_gadget(otg, 0);
+ }
+
+ if (ret)
+ return ret;
+
+ /* start new protocol */
+ if (protocol == PROTO_HOST) {
+ ret = otg_start_host(otg, 1);
+ } else if (protocol == PROTO_GADGET) {
+ ret = otg_start_gadget(otg, 1);
+ otg->gadget_ops->connect_control(otg->gadget, true);
+ }
+
+ if (ret)
+ return ret;
+
+ fsm->protocol = protocol;
+ return 0;
+ }
+
+ return 0;
+}
+
+/**
+ * drd_set_state() - Set the DRD state machine state.
+ * @fsm: DRD FSM instance
+ * @new_state: the new state the DRD FSM must be set to
+ *
+ * Sets the state of the DRD state machine.
+ * fsm->lock must be held.
+ */
+static void drd_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state)
+{
+ struct usb_otg *otg = container_of(fsm, struct usb_otg, fsm);
+
+ if (otg->state == new_state)
+ return;
+
+ fsm->state_changed = 1;
+ dev_dbg(otg->dev, "otg: set state: %s\n",
+ usb_otg_state_string(new_state));
+ switch (new_state) {
+ case OTG_STATE_B_IDLE:
+ drd_set_protocol(fsm, PROTO_UNDEF);
+ otg_drv_vbus(otg, 0);
+ break;
+ case OTG_STATE_B_PERIPHERAL:
+ drd_set_protocol(fsm, PROTO_GADGET);
+ otg_drv_vbus(otg, 0);
+ break;
+ case OTG_STATE_A_HOST:
+ drd_set_protocol(fsm, PROTO_HOST);
+ otg_drv_vbus(otg, 1);
+ break;
+ default:
+ dev_warn(otg->dev, "%s: otg: invalid state: %s\n",
+ __func__, usb_otg_state_string(new_state));
+ break;
+ }
+
+ otg->state = new_state;
+}
+
+/**
+ * drd_statemachine() - DRD state change judgement
+ * @otg: usb_otg instance
+ *
+ * Checks the state machine inputs and state and makes a state change
+ * if required.
+ *
+ * For DRD we're only interested in some of the OTG states
+ * i.e. OTG_STATE_B_IDLE: both peripheral and host are stopped
+ * OTG_STATE_B_PERIPHERAL: peripheral active
+ * OTG_STATE_A_HOST: host active
+ * we're only interested in the following inputs
+ * fsm->id, fsm->b_sess_vld
+ *
+ * Return: 0 if state wasn't changed, 1 if state changed.
+ */
+int drd_statemachine(struct usb_otg *otg)
+{
+ struct otg_fsm *fsm = &otg->fsm;
+ enum usb_otg_state state;
+ int ret;
+
+ mutex_lock(&fsm->lock);
+
+ fsm->state_changed = 0;
+ state = otg->state;
+
+ switch (state) {
+ case OTG_STATE_UNDEFINED:
+ if (!fsm->id)
+ drd_set_state(fsm, OTG_STATE_A_HOST);
+ else if (fsm->id && fsm->b_sess_vld)
+ drd_set_state(fsm, OTG_STATE_B_PERIPHERAL);
+ else
+ drd_set_state(fsm, OTG_STATE_B_IDLE);
+ break;
+ case OTG_STATE_B_IDLE:
+ if (!fsm->id)
+ drd_set_state(fsm, OTG_STATE_A_HOST);
+ else if (fsm->b_sess_vld)
+ drd_set_state(fsm, OTG_STATE_B_PERIPHERAL);
+ break;
+ case OTG_STATE_B_PERIPHERAL:
+ if (!fsm->id)
+ drd_set_state(fsm, OTG_STATE_A_HOST);
+ else if (!fsm->b_sess_vld)
+ drd_set_state(fsm, OTG_STATE_B_IDLE);
+ break;
+ case OTG_STATE_A_HOST:
+ if (fsm->id && fsm->b_sess_vld)
+ drd_set_state(fsm, OTG_STATE_B_PERIPHERAL);
+ else if (fsm->id && !fsm->b_sess_vld)
+ drd_set_state(fsm, OTG_STATE_B_IDLE);
+ break;
+
+ default:
+ dev_err(otg->dev, "%s: otg: invalid usb-drd state: %s\n",
+ __func__, usb_otg_state_string(state));
+ break;
+ }
+
+ ret = fsm->state_changed;
+ mutex_unlock(&fsm->lock);
+ dev_dbg(otg->dev, "otg: quit statemachine, changed %d\n",
+ fsm->state_changed);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(drd_statemachine);
+
+/**
+ * usb_drd_work() - Dual-role state machine work function
+ * @work: work_struct context
+ *
+ * Runs the DRD state machine. Scheduled whenever there is a change
+ * in FSM inputs.
+ */
+static void usb_drd_work(struct work_struct *work)
+{
+ struct usb_otg *otg = container_of(work, struct usb_otg, work);
+
+ pm_runtime_get_sync(otg->dev);
+ while (drd_statemachine(otg))
+ ;
+ pm_runtime_put_sync(otg->dev);
+}
+
+/**
+ * usb_otg_register() - Register the OTG/dual-role device to OTG core
+ * @dev: OTG/dual-role controller device.
+ * @config: OTG configuration.
+ *
+ * Registers the OTG/dual-role controller device with the USB OTG core.
+ *
+ * Return: struct usb_otg * if success, ERR_PTR() otherwise.
+ */
+struct usb_otg *usb_otg_register(struct device *dev,
+ struct usb_otg_config *config)
+{
+ struct usb_otg *otg;
+ int ret = 0;
+
+ if (!dev || !config || !config->fsm_ops)
+ return ERR_PTR(-EINVAL);
+
+ /* already in list? */
+ mutex_lock(&otg_list_mutex);
+ if (usb_otg_get_data(dev)) {
+ dev_err(dev, "otg: %s: device already in otg list\n",
+ __func__);
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ /* allocate and add to list */
+ otg = kzalloc(sizeof(*otg), GFP_KERNEL);
+ if (!otg) {
+ ret = -ENOMEM;
+ goto unlock;
+ }
+
+ otg->dev = dev;
+ /* otg->caps is controller caps + DT overrides */
+ otg->caps = *config->otg_caps;
+ ret = of_usb_update_otg_caps(dev->of_node, &otg->caps);
+ if (ret)
+ goto err_wq;
+
+ if ((otg->caps.hnp_support || otg->caps.srp_support ||
+ otg->caps.adp_support) && !config->otg_work) {
+ dev_err(dev,
+ "otg: otg_work must be provided for OTG support\n");
+ ret = -EINVAL;
+ goto err_wq;
+ }
+
+ if (config->otg_work) /* custom otg_work ? */
+ INIT_WORK(&otg->work, config->otg_work);
+ else
+ INIT_WORK(&otg->work, usb_drd_work);
+
+ otg->wq = create_freezable_workqueue("usb_otg");
+ if (!otg->wq) {
+ dev_err(dev, "otg: %s: can't create workqueue\n",
+ __func__);
+ ret = -ENOMEM;
+ goto err_wq;
+ }
+
+ /* set otg ops */
+ otg->fsm.ops = config->fsm_ops;
+
+ mutex_init(&otg->fsm.lock);
+
+ list_add_tail(&otg->list, &otg_list);
+ mutex_unlock(&otg_list_mutex);
+
+ return otg;
+
+err_wq:
+ kfree(otg);
+unlock:
+ mutex_unlock(&otg_list_mutex);
+ return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(usb_otg_register);
+
+/**
+ * usb_otg_unregister() - Unregister the OTG/dual-role device from USB OTG core
+ * @dev: OTG controller device.
+ *
+ * Unregisters the OTG/dual-role controller device from USB OTG core.
+ * Prevents unregistering till both the associated Host and Gadget controllers
+ * have unregistered from the OTG core.
+ *
+ * Return: 0 on success, error value otherwise.
+ */
+int usb_otg_unregister(struct device *dev)
+{
+ struct usb_otg *otg;
+
+ mutex_lock(&otg_list_mutex);
+ otg = usb_otg_get_data(dev);
+ if (!otg) {
+ dev_err(dev, "otg: %s: device not in otg list\n",
+ __func__);
+ mutex_unlock(&otg_list_mutex);
+ return -EINVAL;
+ }
+
+ /* prevent unregister till both host & gadget have unregistered */
+ if (otg->host || otg->gadget) {
+ dev_err(dev, "otg: %s: host/gadget still registered\n",
+ __func__);
+ mutex_unlock(&otg_list_mutex);
+ return -EBUSY;
+ }
+
+ /* OTG FSM is halted when host/gadget unregistered */
+ destroy_workqueue(otg->wq);
+
+ /* remove from otg list */
+ list_del(&otg->list);
+ kfree(otg);
+ mutex_unlock(&otg_list_mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usb_otg_unregister);
+
+/**
+ * usb_otg_start_fsm() - Start the OTG FSM
+ * @otg: usb_otg instance
+ *
+ * Start the OTG FSM if we can. The HCD, UDC and gadget function driver
+ * must be ready for the OTG FSM to start.
+ *
+ * fsm->lock must be held.
+ */
+static void usb_otg_start_fsm(struct usb_otg *otg)
+{
+ struct otg_fsm *fsm = &otg->fsm;
+
+ if (fsm->running)
+ goto kick_fsm;
+
+ if (!otg->host) {
+ dev_info(otg->dev, "otg: can't start till host registers\n");
+ return;
+ }
+
+ if (!otg->gadget) {
+ dev_info(otg->dev,
+ "otg: can't start till gadget UDC registers\n");
+ return;
+ }
+
+ if (!otg->gadget_ready) {
+ dev_info(otg->dev,
+ "otg: can't start till gadget function registers\n");
+ return;
+ }
+
+ fsm->running = true;
+kick_fsm:
+ queue_work(otg->wq, &otg->work);
+}
+
+/**
+ * usb_otg_stop_fsm() - Stop the OTG FSM
+ * @otg: usb_otg instance
+ *
+ * Stops the HCD, UDC and the OTG FSM.
+ *
+ * fsm->lock must be held.
+ */
+static void usb_otg_stop_fsm(struct usb_otg *otg)
+{
+ struct otg_fsm *fsm = &otg->fsm;
+
+ if (!fsm->running)
+ return;
+
+ /* no more new events queued */
+ fsm->running = false;
+
+ flush_workqueue(otg->wq);
+ otg->state = OTG_STATE_UNDEFINED;
+
+ /* stop host/gadget immediately */
+ if (fsm->protocol == PROTO_HOST) {
+ otg_start_host(otg, 0);
+ } else if (fsm->protocol == PROTO_GADGET) {
+ otg->gadget_ops->connect_control(otg->gadget, false);
+ otg_start_gadget(otg, 0);
+ }
+ fsm->protocol = PROTO_UNDEF;
+}
+
+/**
+ * usb_otg_sync_inputs() - Sync OTG inputs with the OTG state machine
+ * @otg: usb_otg instance
+ *
+ * Used by the OTG driver to update the inputs to the OTG
+ * state machine.
+ *
+ * Can be called in IRQ context.
+ */
+void usb_otg_sync_inputs(struct usb_otg *otg)
+{
+ /* Don't kick FSM till it has started */
+ if (!otg->fsm.running)
+ return;
+
+ /* Kick FSM */
+ queue_work(otg->wq, &otg->work);
+}
+EXPORT_SYMBOL_GPL(usb_otg_sync_inputs);
+
+/**
+ * usb_otg_kick_fsm() - Kick the OTG state machine
+ * @otg_dev: OTG controller device
+ *
+ * Used by USB host/gadget stack to sync OTG related
+ * events to the OTG state machine.
+ * e.g. change in host_bus->b_hnp_enable, gadget->b_hnp_enable
+ *
+ * Return: 0 on success, error value otherwise.
+ */
+int usb_otg_kick_fsm(struct device *otg_dev)
+{
+ struct usb_otg *otg;
+
+ mutex_lock(&otg_list_mutex);
+ otg = usb_otg_get_data(otg_dev);
+ mutex_unlock(&otg_list_mutex);
+ if (!otg) {
+ dev_dbg(otg_dev, "otg: %s: invalid otg device\n",
+ __func__);
+ return -ENODEV;
+ }
+
+ usb_otg_sync_inputs(otg);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usb_otg_kick_fsm);
+
+/**
+ * usb_otg_register_hcd() - Register the host controller to OTG core
+ * @hcd: host controller
+ * @irqnum: interrupt number
+ * @irqflags: interrupt flags
+ * @ops: HCD ops to interface with the HCD
+ *
+ * This is used by the USB Host stack to register the host controller
+ * to the OTG core. Host controller must not be started by the
+ * caller as it is left upto the OTG state machine to do so.
+ * hcd->otg_dev must contain the related otg controller device.
+ *
+ * Return: 0 on success, error value otherwise.
+ */
+int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
+ unsigned long irqflags, struct otg_hcd_ops *ops)
+{
+ struct usb_otg *otg;
+ struct device *hcd_dev = hcd->self.controller;
+ struct device *otg_dev = hcd->otg_dev;
+
+ if (!otg_dev)
+ return -EINVAL;
+
+ /* we're otg but otg controller might not yet be registered */
+ mutex_lock(&otg_list_mutex);
+ otg = usb_otg_get_data(otg_dev);
+ mutex_unlock(&otg_list_mutex);
+ if (!otg) {
+ dev_dbg(hcd_dev,
+ "otg: controller not yet registered. deferring.\n");
+ return -EPROBE_DEFER;
+ }
+
+ /* HCD will be started by OTG fsm when needed */
+ mutex_lock(&otg->fsm.lock);
+ if (otg->primary_hcd.hcd) {
+ /* probably a shared HCD ? */
+ if (usb_otg_hcd_is_primary_hcd(hcd)) {
+ dev_err(otg_dev, "otg: primary host already registered\n");
+ goto err;
+ }
+
+ if (hcd->shared_hcd == otg->primary_hcd.hcd) {
+ if (otg->shared_hcd.hcd) {
+ dev_err(otg_dev, "otg: shared host already registered\n");
+ goto err;
+ }
+
+ otg->shared_hcd.hcd = hcd;
+ otg->shared_hcd.irqnum = irqnum;
+ otg->shared_hcd.irqflags = irqflags;
+ otg->shared_hcd.ops = ops;
+ dev_info(otg_dev, "otg: shared host %s registered\n",
+ dev_name(hcd->self.controller));
+ } else {
+ dev_err(otg_dev, "otg: invalid shared host %s\n",
+ dev_name(hcd->self.controller));
+ goto err;
+ }
+ } else {
+ if (!usb_otg_hcd_is_primary_hcd(hcd)) {
+ dev_err(otg_dev, "otg: primary host must be registered first\n");
+ goto err;
+ }
+
+ otg->primary_hcd.hcd = hcd;
+ otg->primary_hcd.irqnum = irqnum;
+ otg->primary_hcd.irqflags = irqflags;
+ otg->primary_hcd.ops = ops;
+ otg->hcd_ops = ops;
+ dev_info(otg_dev, "otg: primary host %s registered\n",
+ dev_name(hcd->self.controller));
+ }
+
+ /*
+ * we're ready only if we have shared HCD
+ * or we don't need shared HCD.
+ */
+ if (otg->shared_hcd.hcd || !otg->primary_hcd.hcd->shared_hcd) {
+ otg->host = hcd_to_bus(hcd);
+ /* FIXME: set bus->otg_port if this is true OTG port with HNP */
+
+ /* start FSM */
+ usb_otg_start_fsm(otg);
+ } else {
+ dev_dbg(otg_dev, "otg: can't start till shared host registers\n");
+ }
+
+ mutex_unlock(&otg->fsm.lock);
+
+ return 0;
+
+err:
+ mutex_unlock(&otg->fsm.lock);
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(usb_otg_register_hcd);
+
+/**
+ * usb_otg_unregister_hcd() - Unregister the host controller from OTG core
+ * @hcd: host controller device
+ *
+ * This is used by the USB Host stack to unregister the host controller
+ * from the OTG core. Ensures that host controller is not running
+ * on successful return.
+ *
+ * Returns: 0 on success, error value otherwise.
+ */
+int usb_otg_unregister_hcd(struct usb_hcd *hcd)
+{
+ struct usb_otg *otg;
+ struct device *hcd_dev = hcd_to_bus(hcd)->controller;
+ struct device *otg_dev = hcd->otg_dev;
+
+ if (!otg_dev)
+ return -EINVAL; /* we're definitely not OTG */
+
+ mutex_lock(&otg_list_mutex);
+ otg = usb_otg_get_data(otg_dev);
+ mutex_unlock(&otg_list_mutex);
+ if (!otg) {
+ dev_err(hcd_dev, "otg: host %s wasn't registered with otg\n",
+ dev_name(hcd_dev));
+ return -EINVAL;
+ }
+
+ mutex_lock(&otg->fsm.lock);
+ if (hcd == otg->primary_hcd.hcd) {
+ otg->primary_hcd.hcd = NULL;
+ dev_info(otg_dev, "otg: primary host %s unregistered\n",
+ dev_name(hcd_dev));
+ } else if (hcd == otg->shared_hcd.hcd) {
+ otg->shared_hcd.hcd = NULL;
+ dev_info(otg_dev, "otg: shared host %s unregistered\n",
+ dev_name(hcd_dev));
+ } else {
+ mutex_unlock(&otg->fsm.lock);
+ dev_err(otg_dev, "otg: host %s wasn't registered with otg\n",
+ dev_name(hcd_dev));
+ return -EINVAL;
+ }
+
+ /* stop FSM & Host */
+ usb_otg_stop_fsm(otg);
+ otg->host = NULL;
+
+ mutex_unlock(&otg->fsm.lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usb_otg_unregister_hcd);
+
+/**
+ * usb_otg_register_gadget() - Register the gadget controller to OTG core
+ * @gadget: gadget controller instance
+ * @ops: gadget interface ops
+ *
+ * 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.
+ *
+ * Returns: 0 on success, error value otherwise.
+ */
+int usb_otg_register_gadget(struct usb_gadget *gadget,
+ struct otg_gadget_ops *ops)
+{
+ struct usb_otg *otg;
+ struct device *gadget_dev = &gadget->dev;
+ struct device *otg_dev = gadget->otg_dev;
+
+ if (!otg_dev)
+ return -EINVAL; /* we're definitely not OTG */
+
+ /* we're otg but otg controller might not yet be registered */
+ mutex_lock(&otg_list_mutex);
+ otg = usb_otg_get_data(otg_dev);
+ mutex_unlock(&otg_list_mutex);
+ if (!otg) {
+ dev_dbg(gadget_dev,
+ "otg: controller not yet registered, deferring.\n");
+ return -EPROBE_DEFER;
+ }
+
+ mutex_lock(&otg->fsm.lock);
+ if (otg->gadget) {
+ dev_err(otg_dev, "otg: gadget already registered with otg\n");
+ mutex_unlock(&otg->fsm.lock);
+ return -EINVAL;
+ }
+
+ otg->gadget = gadget;
+ otg->gadget_ops = ops;
+ dev_info(otg_dev, "otg: gadget %s registered\n",
+ dev_name(&gadget->dev));
+
+ /* FSM will be started in usb_otg_gadget_ready() */
+ mutex_unlock(&otg->fsm.lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usb_otg_register_gadget);
+
+/**
+ * usb_otg_unregister_gadget() - Unregister the gadget controller from OTG core
+ * @gadget: gadget controller
+ *
+ * This is used by the USB gadget stack to unregister the gadget controller
+ * from the OTG core. Ensures that gadget controller is not running
+ * on successful return.
+ *
+ * Returns: 0 on success, error value otherwise.
+ */
+int usb_otg_unregister_gadget(struct usb_gadget *gadget)
+{
+ struct usb_otg *otg;
+ struct device *gadget_dev = &gadget->dev;
+ struct device *otg_dev = gadget->otg_dev;
+
+ if (!otg_dev)
+ return -EINVAL;
+
+ mutex_lock(&otg_list_mutex);
+ otg = usb_otg_get_data(otg_dev);
+ mutex_unlock(&otg_list_mutex);
+ if (!otg) {
+ dev_err(gadget_dev,
+ "otg: gadget %s wasn't registered with otg\n",
+ dev_name(&gadget->dev));
+ return -EINVAL;
+ }
+
+ mutex_lock(&otg->fsm.lock);
+ if (otg->gadget != gadget) {
+ mutex_unlock(&otg->fsm.lock);
+ dev_err(otg_dev, "otg: gadget %s wasn't registered with otg\n",
+ dev_name(&gadget->dev));
+ return -EINVAL;
+ }
+
+ /* FSM must be stopped in usb_otg_gadget_ready() */
+ if (otg->gadget_ready) {
+ dev_err(otg_dev,
+ "otg: gadget %s unregistered before being unready, forcing stop\n",
+ dev_name(&gadget->dev));
+ usb_otg_stop_fsm(otg);
+ }
+
+ otg->gadget = NULL;
+ mutex_unlock(&otg->fsm.lock);
+
+ dev_info(otg_dev, "otg: gadget %s unregistered\n",
+ dev_name(&gadget->dev));
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usb_otg_unregister_gadget);
+
+/**
+ * usb_otg_gadget_ready() - Notify gadget function driver ready status
+ * @gadget: gadget controller
+ * @ready: 0: function driver ready, 1: function driver not ready
+ *
+ * Notify the OTG core about status of the gadget function driver.
+ * As OTG core is responsible to start/stop the gadget controller, it
+ * must be aware when the gadget function driver is available or not.
+ * This function is used by the Gadget core to inform the OTG core
+ * about the gadget function driver readyness.
+ *
+ * Return: 0 on sucess, error value otherwise.
+ */
+int usb_otg_gadget_ready(struct usb_gadget *gadget, bool ready)
+{
+ struct usb_otg *otg;
+ struct device *gadget_dev = &gadget->dev;
+ struct device *otg_dev = gadget->otg_dev;
+
+ if (!otg_dev)
+ return -EINVAL;
+
+ mutex_lock(&otg_list_mutex);
+ otg = usb_otg_get_data(otg_dev);
+ mutex_unlock(&otg_list_mutex);
+ if (!otg) {
+ dev_err(gadget_dev,
+ "otg: gadget %s wasn't registered with otg\n",
+ dev_name(&gadget->dev));
+ return -EINVAL;
+ }
+
+ mutex_lock(&otg->fsm.lock);
+ if (otg->gadget != gadget) {
+ mutex_unlock(&otg->fsm.lock);
+ dev_err(otg_dev, "otg: gadget %s wasn't registered with otg\n",
+ dev_name(&gadget->dev));
+ return -EINVAL;
+ }
+
+ /* Start/stop FSM & gadget */
+ otg->gadget_ready = ready;
+ if (ready)
+ usb_otg_start_fsm(otg);
+ else
+ usb_otg_stop_fsm(otg);
+
+ dev_dbg(otg_dev, "otg: gadget %s %sready\n", dev_name(&gadget->dev),
+ ready ? "" : "not ");
+
+ mutex_unlock(&otg->fsm.lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usb_otg_gadget_ready);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/core/Kconfig b/drivers/usb/core/Kconfig
index ae228d0..37f8c54 100644
--- a/drivers/usb/core/Kconfig
+++ b/drivers/usb/core/Kconfig
@@ -41,20 +41,6 @@ config USB_DYNAMIC_MINORS
If you are unsure about this, say N here.
-config USB_OTG
- bool "OTG support"
- depends on PM
- default n
- help
- The most notable feature of USB OTG is support for a
- "Dual-Role" device, which can act as either a device
- or a host. The initial role is decided by the type of
- plug inserted and can be changed later when two dual
- role devices talk to each other.
-
- Select this only if your board has Mini-AB/Micro-AB
- connector.
-
config USB_OTG_WHITELIST
bool "Rely on OTG and EH Targeted Peripherals List"
depends on USB
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 3c3f31c..5fc9095 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -16,6 +16,7 @@
menuconfig USB_GADGET
tristate "USB Gadget Support"
select USB_COMMON
+ select USB_OTG_CORE
select NLS
help
USB is a master/slave protocol, organized with one master
diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h
index f4fc0aa..1d74fb8 100644
--- a/include/linux/usb/gadget.h
+++ b/include/linux/usb/gadget.h
@@ -328,6 +328,7 @@ struct usb_gadget_ops {
* @in_epnum: last used in ep number
* @mA: last set mA value
* @otg_caps: OTG capabilities of this gadget.
+ * @otg_dev: OTG controller device, if needs to be used with OTG core.
* @sg_supported: true if we can handle scatter-gather
* @is_otg: True if the USB device port uses a Mini-AB jack, so that the
* gadget driver must provide a USB OTG descriptor.
@@ -385,6 +386,7 @@ struct usb_gadget {
unsigned in_epnum;
unsigned mA;
struct usb_otg_caps *otg_caps;
+ struct device *otg_dev;
unsigned sg_supported:1;
unsigned is_otg:1;
diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h
index 7729c1f..36bd54f 100644
--- a/include/linux/usb/hcd.h
+++ b/include/linux/usb/hcd.h
@@ -185,6 +185,7 @@ struct usb_hcd {
struct mutex *bandwidth_mutex;
struct usb_hcd *shared_hcd;
struct usb_hcd *primary_hcd;
+ struct device *otg_dev; /* OTG controller device */
#define HCD_BUFFER_POOLS 4
diff --git a/include/linux/usb/otg-fsm.h b/include/linux/usb/otg-fsm.h
index 26e6531..943714a 100644
--- a/include/linux/usb/otg-fsm.h
+++ b/include/linux/usb/otg-fsm.h
@@ -60,6 +60,11 @@ enum otg_fsm_timer {
/**
* struct otg_fsm - OTG state machine according to the OTG spec
*
+ * DRD mode hardware Inputs
+ *
+ * @id: TRUE for B-device, FALSE for A-device.
+ * @b_sess_vld: VBUS voltage in regulation.
+ *
* OTG hardware Inputs
*
* Common inputs for A and B device
@@ -132,6 +137,7 @@ enum otg_fsm_timer {
* a_clr_err: Asserted (by application ?) to clear a_vbus_err due to an
* overcurrent condition and causes the A-device to transition
* to a_wait_vfall
+ * running: state machine running/stopped indicator
*/
struct otg_fsm {
/* Input */
@@ -187,6 +193,7 @@ struct otg_fsm {
int b_ase0_brst_tmout;
int a_bidl_adis_tmout;
+ bool running;
struct otg_fsm_ops *ops;
/* Current usb protocol used: 0:undefine; 1:host; 2:client */
diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h
index 85b8fb5..2175fb7 100644
--- a/include/linux/usb/otg.h
+++ b/include/linux/usb/otg.h
@@ -10,10 +10,67 @@
#define __LINUX_USB_OTG_H
#include <linux/phy/phy.h>
-#include <linux/usb/phy.h>
-#include <linux/usb/otg-fsm.h>
+#include <linux/device.h>
+#include <linux/usb.h>
#include <linux/usb/hcd.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/otg-fsm.h>
+#include <linux/usb/phy.h>
+
+/**
+ * struct otg_hcd - host controller state and interface
+ *
+ * @hcd: host controller
+ * @irqnum: IRQ number
+ * @irqflags: IRQ flags
+ * @ops: OTG to host controller interface
+ * @otg_dev: OTG controller device
+ */
+struct otg_hcd {
+ struct usb_hcd *hcd;
+ unsigned int irqnum;
+ unsigned long irqflags;
+ struct otg_hcd_ops *ops;
+ struct device *otg_dev;
+};
+
+/**
+ * struct usb_otg_caps - describes the otg capabilities of the device
+ * @otg_rev: The OTG revision number the device is compliant with, it's
+ * in binary-coded decimal (i.e. 2.0 is 0200H).
+ * @hnp_support: Indicates if the device supports HNP.
+ * @srp_support: Indicates if the device supports SRP.
+ * @adp_support: Indicates if the device supports ADP.
+ */
+struct usb_otg_caps {
+ u16 otg_rev;
+ bool hnp_support;
+ bool srp_support;
+ bool adp_support;
+};
+/**
+ * struct usb_otg - usb otg controller state
+ *
+ * @default_a: Indicates we are an A device. i.e. Host.
+ * @phy: USB PHY interface
+ * @usb_phy: old usb_phy interface
+ * @host: host controller bus
+ * @gadget: gadget device
+ * @state: current OTG state
+ * @dev: OTG controller device
+ * @caps: OTG capabilities revision, hnp, srp, etc
+ * @fsm: OTG finite state machine
+ * @hcd_ops: host controller interface
+ * ------- internal use only -------
+ * @primary_hcd: primary host state and interface
+ * @shared_hcd: shared host state and interface
+ * @gadget_ops: gadget controller interface
+ * @list: list of OTG controllers
+ * @work: OTG state machine work
+ * @wq: OTG state machine work queue
+ * @flags: to track if host/gadget is running
+ */
struct usb_otg {
u8 default_a;
@@ -24,9 +81,25 @@ struct usb_otg {
struct usb_gadget *gadget;
enum usb_otg_state state;
+ struct device *dev;
+ struct usb_otg_caps caps;
struct otg_fsm fsm;
struct otg_hcd_ops *hcd_ops;
+ /* internal use only */
+ struct otg_hcd primary_hcd;
+ struct otg_hcd shared_hcd;
+ struct otg_gadget_ops *gadget_ops;
+ bool gadget_ready;
+ struct list_head list;
+ struct work_struct work;
+ struct workqueue_struct *wq;
+ u32 flags;
+#define OTG_FLAG_GADGET_RUNNING (1 << 0)
+#define OTG_FLAG_HOST_RUNNING (1 << 1)
+ /* use otg->fsm.lock for serializing access */
+
+/*------------- deprecated interface -----------------------------*/
/* bind/unbind the host controller */
int (*set_host)(struct usb_otg *otg, struct usb_bus *host);
@@ -42,26 +115,101 @@ struct usb_otg {
/* start or continue HNP role switch */
int (*start_hnp)(struct usb_otg *otg);
-
+/*---------------------------------------------------------------*/
};
/**
- * struct usb_otg_caps - describes the otg capabilities of the device
- * @otg_rev: The OTG revision number the device is compliant with, it's
- * in binary-coded decimal (i.e. 2.0 is 0200H).
- * @hnp_support: Indicates if the device supports HNP.
- * @srp_support: Indicates if the device supports SRP.
- * @adp_support: Indicates if the device supports ADP.
+ * struct usb_otg_config - OTG controller configuration
+ * @caps: OTG capabilities of the controller
+ * @ops: OTG FSM operations
+ * @otg_work: optional custom OTG state machine work function
*/
-struct usb_otg_caps {
- u16 otg_rev;
- bool hnp_support;
- bool srp_support;
- bool adp_support;
+struct usb_otg_config {
+ struct usb_otg_caps *otg_caps;
+ struct otg_fsm_ops *fsm_ops;
+ void (*otg_work)(struct work_struct *work);
};
extern const char *usb_otg_state_string(enum usb_otg_state state);
+#if IS_ENABLED(CONFIG_USB_OTG)
+struct usb_otg *usb_otg_register(struct device *dev,
+ struct usb_otg_config *config);
+int usb_otg_unregister(struct device *dev);
+int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
+ unsigned long irqflags, struct otg_hcd_ops *ops);
+int usb_otg_unregister_hcd(struct usb_hcd *hcd);
+int usb_otg_register_gadget(struct usb_gadget *gadget,
+ struct otg_gadget_ops *ops);
+int usb_otg_unregister_gadget(struct usb_gadget *gadget);
+void usb_otg_sync_inputs(struct usb_otg *otg);
+int usb_otg_kick_fsm(struct device *otg_dev);
+int usb_otg_start_host(struct usb_otg *otg, int on);
+int usb_otg_start_gadget(struct usb_otg *otg, int on);
+int usb_otg_gadget_ready(struct usb_gadget *gadget, bool ready);
+
+#else /* CONFIG_USB_OTG */
+
+static inline struct usb_otg *usb_otg_register(struct device *dev,
+ struct usb_otg_config *config)
+{
+ return ERR_PTR(-ENOTSUPP);
+}
+
+static inline int usb_otg_unregister(struct device *dev)
+{
+ return -ENOTSUPP;
+}
+
+static inline int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
+ unsigned long irqflags,
+ struct otg_hcd_ops *ops)
+{
+ return -ENOTSUPP;
+}
+
+static inline int usb_otg_unregister_hcd(struct usb_hcd *hcd)
+{
+ return -ENOTSUPP;
+}
+
+static inline int usb_otg_register_gadget(struct usb_gadget *gadget,
+ struct otg_gadget_ops *ops)
+{
+ return -ENOTSUPP;
+}
+
+static inline int usb_otg_unregister_gadget(struct usb_gadget *gadget)
+{
+ return -ENOTSUPP;
+}
+
+static inline void usb_otg_sync_inputs(struct usb_otg *otg)
+{
+}
+
+static inline int usb_otg_kick_fsm(struct device *otg_dev)
+{
+ return -ENOTSUPP;
+}
+
+static inline int usb_otg_start_host(struct usb_otg *otg, int on)
+{
+ return -ENOTSUPP;
+}
+
+static inline int usb_otg_start_gadget(struct usb_otg *otg, int on)
+{
+ return -ENOTSUPP;
+}
+
+static inline int usb_otg_gadget_ready(struct usb_gadget *gadget, bool ready)
+{
+ return -ENOTSUPP;
+}
+#endif /* CONFIG_USB_OTG */
+
+/*------------- deprecated interface -----------------------------*/
/* Context: can sleep */
static inline int
otg_start_hnp(struct usb_otg *otg)
@@ -113,6 +261,8 @@ otg_start_srp(struct usb_otg *otg)
return -ENOTSUPP;
}
+/*---------------------------------------------------------------*/
+
/* for OTG controller drivers (and maybe other stuff) */
extern int usb_bus_start_enum(struct usb_bus *bus, unsigned port_num);
@@ -237,4 +387,6 @@ static inline int otg_start_gadget(struct usb_otg *otg, int on)
return otg->fsm.ops->start_gadget(otg, on);
}
+int drd_statemachine(struct usb_otg *otg);
+
#endif /* __LINUX_USB_OTG_H */
--
2.7.4
[toc] | [prev] | [next] | [standalone]
| From | Roger Quadros <rogerq@ti.com> |
|---|---|
| Date | 2016-06-10 15:20 +0200 |
| Subject | [PATCH v10 03/14] usb: hcd.h: Add OTG to HCD interface |
| Message-ID | <rIuvU-527-17@gated-at.bofh.it> |
| In reply to | #1419385 |
The OTG core will use struct otg_hcd_ops to interface
with the HCD (Host Controller Driver).
The main purpose of this interface is to avoid directly
calling HCD APIs from the OTG core as they
wouldn't be defined in the built-in symbol table if
CONFIG_USB is m.
Signed-off-by: Roger Quadros <rogerq@ti.com>
Acked-by: Peter Chen <peter.chen@nxp.com>
---
include/linux/usb/hcd.h | 24 ++++++++++++++++++++++++
1 file changed, 24 insertions(+)
diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h
index 66fc137..7729c1f 100644
--- a/include/linux/usb/hcd.h
+++ b/include/linux/usb/hcd.h
@@ -400,6 +400,30 @@ struct hc_driver {
};
+/**
+ * struct otg_hcd_ops - Interface between OTG core and HCD
+ *
+ * Provided by the HCD core to allow the OTG core to interface with the HCD
+ *
+ * @add: function to add the HCD
+ * @remove: function to remove the HCD
+ * @usb_bus_start_enum: function to immediately start bus enumeration
+ * @usb_control_msg: function to build and send a control URB
+ * @usb_hub_find_child: function to get pointer to the child device
+ */
+struct otg_hcd_ops {
+ int (*add)(struct usb_hcd *hcd,
+ unsigned int irqnum, unsigned long irqflags);
+ void (*remove)(struct usb_hcd *hcd);
+ int (*usb_bus_start_enum)(struct usb_bus *bus, unsigned int port_num);
+ int (*usb_control_msg)(struct usb_device *dev, unsigned int pipe,
+ __u8 request, __u8 requesttype, __u16 value,
+ __u16 index, void *data, __u16 size,
+ int timeout);
+ struct usb_device * (*usb_hub_find_child)(struct usb_device *hdev,
+ int port1);
+};
+
static inline int hcd_giveback_urb_in_bh(struct usb_hcd *hcd)
{
return hcd->driver->flags & HCD_BH;
--
2.7.4
[toc] | [prev] | [next] | [standalone]
| From | Roger Quadros <rogerq@ti.com> |
|---|---|
| Date | 2016-06-10 15:20 +0200 |
| Subject | [PATCH v10 10/14] usb: otg: add hcd companion support |
| Message-ID | <rIuvU-527-29@gated-at.bofh.it> |
| In reply to | #1419385 |
From: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
Since some host controller (e.g. EHCI) needs a companion host controller
(e.g. OHCI), this patch adds such a configuration to use it in the OTG
core.
Signed-off-by: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
Signed-off-by: Roger Quadros <rogerq@ti.com>
Acked-by: Peter Chen <peter.chen@nxp.com>
Acked-by: Rob Herring <robh@kernel.org>
---
Documentation/devicetree/bindings/usb/generic.txt | 3 +++
drivers/usb/common/usb-otg.c | 31 ++++++++++++++++-------
include/linux/usb/otg.h | 7 ++++-
3 files changed, 31 insertions(+), 10 deletions(-)
diff --git a/Documentation/devicetree/bindings/usb/generic.txt b/Documentation/devicetree/bindings/usb/generic.txt
index f6866c1..1db1c33 100644
--- a/Documentation/devicetree/bindings/usb/generic.txt
+++ b/Documentation/devicetree/bindings/usb/generic.txt
@@ -27,6 +27,9 @@ Optional properties:
- otg-controller: phandle to otg controller. Host or gadget controllers can
contain this property to link it to a particular OTG
controller.
+ - hcd-needs-companion: must be present if otg controller is dealing with
+ EHCI host controller that needs a companion OHCI host
+ controller.
This is an attribute to a USB controller such as:
diff --git a/drivers/usb/common/usb-otg.c b/drivers/usb/common/usb-otg.c
index 4185954..c663f4a 100644
--- a/drivers/usb/common/usb-otg.c
+++ b/drivers/usb/common/usb-otg.c
@@ -402,6 +402,10 @@ struct usb_otg *usb_otg_register(struct device *dev,
else
INIT_WORK(&otg->work, usb_drd_work);
+ if (of_find_property(dev->of_node, "hcd-needs-companion", NULL) ||
+ config->hcd_needs_companion) /* needs companion ? */
+ otg->flags |= OTG_FLAG_HCD_NEEDS_COMPANION;
+
otg->wq = create_freezable_workqueue("usb_otg");
if (!otg->wq) {
dev_err(dev, "otg: %s: can't create workqueue\n",
@@ -626,15 +630,18 @@ int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
/* HCD will be started by OTG fsm when needed */
mutex_lock(&otg->fsm.lock);
if (otg->primary_hcd.hcd) {
- /* probably a shared HCD ? */
- if (usb_otg_hcd_is_primary_hcd(hcd)) {
+ /* probably a shared HCD or a companion OHCI HCD ? */
+ if (!(otg->flags & OTG_FLAG_HCD_NEEDS_COMPANION) &&
+ usb_otg_hcd_is_primary_hcd(hcd)) {
dev_err(otg_dev, "otg: primary host already registered\n");
goto err;
}
- if (hcd->shared_hcd == otg->primary_hcd.hcd) {
+ if (otg->flags & OTG_FLAG_HCD_NEEDS_COMPANION ||
+ (hcd->shared_hcd == otg->primary_hcd.hcd)) {
if (otg->shared_hcd.hcd) {
- dev_err(otg_dev, "otg: shared host already registered\n");
+ dev_err(otg_dev,
+ "otg: shared/companion host already registered\n");
goto err;
}
@@ -642,10 +649,12 @@ int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
otg->shared_hcd.irqnum = irqnum;
otg->shared_hcd.irqflags = irqflags;
otg->shared_hcd.ops = ops;
- dev_info(otg_dev, "otg: shared host %s registered\n",
+ dev_info(otg_dev,
+ "otg: shared/companion host %s registered\n",
dev_name(hcd->self.controller));
} else {
- dev_err(otg_dev, "otg: invalid shared host %s\n",
+ dev_err(otg_dev,
+ "otg: invalid shared/companion host %s\n",
dev_name(hcd->self.controller));
goto err;
}
@@ -668,14 +677,17 @@ int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
* we're ready only if we have shared HCD
* or we don't need shared HCD.
*/
- if (otg->shared_hcd.hcd || !otg->primary_hcd.hcd->shared_hcd) {
+ if (otg->shared_hcd.hcd ||
+ (!(otg->flags & OTG_FLAG_HCD_NEEDS_COMPANION) &&
+ !otg->primary_hcd.hcd->shared_hcd)) {
otg->host = hcd_to_bus(hcd);
/* FIXME: set bus->otg_port if this is true OTG port with HNP */
/* start FSM */
usb_otg_start_fsm(otg);
} else {
- dev_dbg(otg_dev, "otg: can't start till shared host registers\n");
+ dev_dbg(otg_dev,
+ "otg: can't start till shared/companion host registers\n");
}
mutex_unlock(&otg->fsm.lock);
@@ -723,7 +735,8 @@ int usb_otg_unregister_hcd(struct usb_hcd *hcd)
dev_name(hcd_dev));
} else if (hcd == otg->shared_hcd.hcd) {
otg->shared_hcd.hcd = NULL;
- dev_info(otg_dev, "otg: shared host %s unregistered\n",
+ dev_info(otg_dev,
+ "otg: shared/companion host %s unregistered\n",
dev_name(hcd_dev));
} else {
mutex_unlock(&otg->fsm.lock);
diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h
index 2175fb7..61d524e 100644
--- a/include/linux/usb/otg.h
+++ b/include/linux/usb/otg.h
@@ -69,7 +69,8 @@ struct usb_otg_caps {
* @list: list of OTG controllers
* @work: OTG state machine work
* @wq: OTG state machine work queue
- * @flags: to track if host/gadget is running
+ * @flags: to track if host/gadget is running, or to indicate if hcd needs
+ * companion
*/
struct usb_otg {
u8 default_a;
@@ -97,6 +98,7 @@ struct usb_otg {
u32 flags;
#define OTG_FLAG_GADGET_RUNNING (1 << 0)
#define OTG_FLAG_HOST_RUNNING (1 << 1)
+#define OTG_FLAG_HCD_NEEDS_COMPANION (1 << 2)
/* use otg->fsm.lock for serializing access */
/*------------- deprecated interface -----------------------------*/
@@ -123,11 +125,14 @@ struct usb_otg {
* @caps: OTG capabilities of the controller
* @ops: OTG FSM operations
* @otg_work: optional custom OTG state machine work function
+ * @hcd_needs_companion: Indicates if host controller needs a companion
+ * controller
*/
struct usb_otg_config {
struct usb_otg_caps *otg_caps;
struct otg_fsm_ops *fsm_ops;
void (*otg_work)(struct work_struct *work);
+ bool hcd_needs_companion;
};
extern const char *usb_otg_state_string(enum usb_otg_state state);
--
2.7.4
[toc] | [prev] | [next] | [standalone]
| From | Roger Quadros <rogerq@ti.com> |
|---|---|
| Date | 2016-06-10 15:20 +0200 |
| Subject | [PATCH v10 09/14] usb: of: add an API to get OTG device from USB controller node |
| Message-ID | <rIuvU-527-35@gated-at.bofh.it> |
| In reply to | #1419385 |
The OTG controller and the USB controller can be linked via the
'otg-controller' property in the USB controller's device node.
of_usb_get_otg() can be used to get the OTG controller device
from the USB controller's device node.
Signed-off-by: Roger Quadros <rogerq@ti.com>
Acked-by: Peter Chen <peter.chen@nxp.com>
Acked-by: Rob Herring <robh@kernel.org>
---
Documentation/devicetree/bindings/usb/generic.txt | 3 +++
drivers/usb/common/common.c | 27 +++++++++++++++++++++++
include/linux/usb/of.h | 9 ++++++++
3 files changed, 39 insertions(+)
diff --git a/Documentation/devicetree/bindings/usb/generic.txt b/Documentation/devicetree/bindings/usb/generic.txt
index bba8257..f6866c1 100644
--- a/Documentation/devicetree/bindings/usb/generic.txt
+++ b/Documentation/devicetree/bindings/usb/generic.txt
@@ -24,6 +24,9 @@ Optional properties:
optional for OTG device.
- adp-disable: tells OTG controllers we want to disable OTG ADP, ADP is
optional for OTG device.
+ - otg-controller: phandle to otg controller. Host or gadget controllers can
+ contain this property to link it to a particular OTG
+ controller.
This is an attribute to a USB controller such as:
diff --git a/drivers/usb/common/common.c b/drivers/usb/common/common.c
index e3d0161..d7ec471 100644
--- a/drivers/usb/common/common.c
+++ b/drivers/usb/common/common.c
@@ -238,6 +238,33 @@ int of_usb_update_otg_caps(struct device_node *np,
}
EXPORT_SYMBOL_GPL(of_usb_update_otg_caps);
+#ifdef CONFIG_USB_OTG
+/**
+ * of_usb_get_otg - get the OTG controller linked to the USB controller
+ * @np: Pointer to the device_node of the USB controller
+ * @otg_caps: Pointer to the target usb_otg_caps to be set
+ *
+ * Returns the OTG controller device or NULL on error.
+ */
+struct device *of_usb_get_otg(struct device_node *np)
+{
+ struct device_node *otg_np;
+ struct platform_device *pdev;
+
+ otg_np = of_parse_phandle(np, "otg-controller", 0);
+ if (!otg_np)
+ return NULL;
+
+ pdev = of_find_device_by_node(otg_np);
+ of_node_put(otg_np);
+ if (!pdev)
+ return NULL;
+
+ return &pdev->dev;
+}
+EXPORT_SYMBOL_GPL(of_usb_get_otg);
+#endif
+
#endif
MODULE_LICENSE("GPL");
diff --git a/include/linux/usb/of.h b/include/linux/usb/of.h
index de3237f..499a4e8 100644
--- a/include/linux/usb/of.h
+++ b/include/linux/usb/of.h
@@ -40,6 +40,15 @@ static inline struct device_node *usb_of_get_child_node
}
#endif
+#if IS_ENABLED(CONFIG_OF) && IS_ENABLED(CONFIG_USB_OTG)
+struct device *of_usb_get_otg(struct device_node *np);
+#else
+static inline struct device *of_usb_get_otg(struct device_node *np)
+{
+ return NULL;
+}
+#endif
+
#if IS_ENABLED(CONFIG_OF) && IS_ENABLED(CONFIG_USB_SUPPORT)
enum usb_phy_interface of_usb_get_phy_mode(struct device_node *np);
#else
--
2.7.4
[toc] | [prev] | [standalone]
Back to top | Article view | linux.kernel
csiph-web