2016-04-05 14:05:51

by Roger Quadros

[permalink] [raw]
Subject: [PATCH v6 00/12] USB OTG/dual-role framework

Hi,

This series centralizes OTG/Dual-role functionality in the kernel.
As of now I've got Dual-role functionality working pretty reliably on
dra7-evm and am437x-gp-evm.

DWC3 controller and platform related patches will be sent separately.

Series is based on v4.6-rc1 and depends on [1]
[1] - OTG fsm cleanup - https://lkml.org/lkml/2016/3/30/186

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

Changelog:
---------
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 (12):
usb: hcd: Initialize hcd->flags to 0
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: hcd: Adapt to OTG core
usb: gadget: udc: adapt to OTG core
usb: doc: dt-binding: Add otg-controller property
usb: core: hub: Notify OTG fsm when A device sets b_hnp_enable
usb: host: xhci-plat: Add otg device to platform data

Documentation/devicetree/bindings/usb/generic.txt | 3 +
Documentation/usb/chipidea.txt | 2 +-
drivers/usb/Makefile | 1 +
drivers/usb/chipidea/Makefile | 2 +-
drivers/usb/chipidea/ci.h | 3 +-
drivers/usb/chipidea/core.c | 12 +-
drivers/usb/chipidea/debug.c | 2 +-
drivers/usb/chipidea/otg_fsm.c | 178 ++--
drivers/usb/chipidea/otg_fsm.h | 2 +-
drivers/usb/chipidea/udc.c | 17 +-
drivers/usb/common/Makefile | 3 +-
drivers/usb/common/usb-otg-fsm.c | 191 ++--
drivers/usb/common/usb-otg.c | 1058 ++++++++++++++++++++
.../usb/{chipidea/otg_fsm.h => common/usb-otg.h} | 61 +-
drivers/usb/core/Kconfig | 10 +-
drivers/usb/core/hcd.c | 72 ++
drivers/usb/core/hub.c | 17 +-
drivers/usb/gadget/udc/udc-core.c | 166 ++-
drivers/usb/host/xhci-plat.c | 35 +-
drivers/usb/phy/Kconfig | 2 +-
drivers/usb/phy/phy-fsl-usb.c | 150 +--
drivers/usb/phy/phy-fsl-usb.h | 3 +-
include/linux/usb/gadget.h | 20 +
include/linux/usb/hcd.h | 29 +
include/linux/usb/otg-fsm.h | 139 +--
include/linux/usb/otg.h | 259 ++++-
include/linux/usb/xhci_pdriver.h | 3 +
27 files changed, 1973 insertions(+), 467 deletions(-)
create mode 100644 drivers/usb/common/usb-otg.c
copy drivers/usb/{chipidea/otg_fsm.h => common/usb-otg.h} (63%)

--
2.5.0


2016-04-05 14:05:58

by Roger Quadros

[permalink] [raw]
Subject: [PATCH v6 02/12] usb: hcd.h: Add OTG to HCD interface

The OTG core will use struct otg_hcd_ops to interface
with the HCD controller.

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 <[email protected]>
---
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 b98f831..861ccaa 100644
--- a/include/linux/usb/hcd.h
+++ b/include/linux/usb/hcd.h
@@ -399,6 +399,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 of 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.5.0

2016-04-05 14:06:08

by Roger Quadros

[permalink] [raw]
Subject: [PATCH v6 01/12] usb: hcd: Initialize hcd->flags to 0

When using the OTG/drd library we can call hcd_add/remove
consecutively without calling hcd_alloc in between so flags can be stale.

If the HC dies due to whatever reason then without this
patch we get the below error on next hcd_add.

[ 91.494257] xhci-hcd xhci-hcd.0.auto: HC died; cleaning up
[ 91.502068] hub 3-0:1.0: state 0 ports 1 chg 0000 evt 0000
[ 91.510240] xhci-hcd xhci-hcd.0.auto: xHCI Host Controller
[ 91.516940] xhci-hcd xhci-hcd.0.auto: new USB bus registered, assigned bus number 4
[ 91.529745] usb usb4: We don't know the algorithms for LPM for this host, disabling LPM.
[ 91.540637] usb usb4: New USB device found, idVendor=1d6b, idProduct=0003
[ 91.757865] irq 254: nobody cared (try booting with the "irqpoll" option)
[ 91.757880] CPU: 0 PID: 68 Comm: kworker/u2:2 Not tainted 4.1.4-00828-g1f0ed8c-dirty #44
[ 91.757885] Hardware name: Generic AM43 (Flattened Device Tree)
[ 91.757914] Workqueue: usb_otg usb_otg_work
[ 91.757921] Backtrace:
[ 91.757954] [<c0012af0>] (dump_backtrace) from [<c0012c8c>] (show_stack+0x18/0x1c)
[ 91.757972] r6:c089d4a4 r5:ffffffff r4:00000000 r3:ee440000
[ 91.757991] [<c0012c74>] (show_stack) from [<c05f7c14>] (dump_stack+0x84/0xd0)
[ 91.758008] [<c05f7b90>] (dump_stack) from [<c0084b30>] (__report_bad_irq+0x28/0xc8)
[ 91.758024] r7:00000000 r6:000000fe r5:00000000 r4:ee514c40
[ 91.758037] [<c0084b08>] (__report_bad_irq) from [<c00850b0>] (note_interrupt+0x24c/0x2ac)
[ 91.758052] r6:000000fe r5:00000000 r4:ee514c40 r3:00000000
[ 91.758065] [<c0084e64>] (note_interrupt) from [<c00828fc>] (handle_irq_event_percpu+0xb0/0x158)
[ 91.758085] r10:ee514c40 r9:c08ce49a r8:000000fe r7:00000000 r6:00000000 r5:00000000
[ 91.758094] r4:00000000 r3:00000000
[ 91.758105] [<c008284c>] (handle_irq_event_percpu) from [<c00829e8>] (handle_irq_event+0x44/0x64)
[ 91.758126] r10:00000001 r9:ee441ab0 r8:ee441bb8 r7:c0858b4c r6:ed174280 r5:ee514ca0
[ 91.758132] r4:ee514c40
[ 91.758144] [<c00829a4>] (handle_irq_event) from [<c0085970>] (handle_fasteoi_irq+0x100/0x1bc)
[ 91.758159] r6:c085dba0 r5:ee514ca0 r4:ee514c40 r3:00000000
[ 91.758171] [<c0085870>] (handle_fasteoi_irq) from [<c0082058>] (generic_handle_irq+0x28/0x38)
[ 91.758186] r7:c0853d40 r6:c0858b4c r5:000000fe r4:000000fe
[ 91.758197] [<c0082030>] (generic_handle_irq) from [<c00821c0>] (__handle_domain_irq+0x98/0x12c)
[ 91.758207] r4:c0853d40 r3:00000100
[ 91.758219] [<c0082128>] (__handle_domain_irq) from [<c00094e0>] (gic_handle_irq+0x28/0x68)
[ 91.758239] r10:00000001 r9:ee441bb8 r8:fa240100 r7:c0858d70 r6:ee441ab0 r5:000000b8
[ 91.758245] r4:fa24010c
[ 91.758264] [<c00094b8>] (gic_handle_irq) from [<c05fd540>] (__irq_svc+0x40/0x74)
[ 91.758271] Exception stack(0xee441ab0 to 0xee441af8)
[ 91.758280] 1aa0: 00000000 c08d2980 ee441ac0 00000000
[ 91.758292] 1ac0: 00000008 00000089 c0858b4c c0858080 00000000 ee441bb8 00000001 ee441b3c
[ 91.758301] 1ae0: 00000101 ee441af8 c02fc418 c0046a1c 20000113 ffffffff
[ 91.758321] r8:00000000 r7:ee441ae4 r6:ffffffff r5:20000113 r4:c0046a1c r3:c02fc418
[ 91.758347] [<c00469a0>] (__do_softirq) from [<c0046eac>] (irq_exit+0xb8/0x104)
[ 91.758367] r10:00000001 r9:ee441bb8 r8:00000000 r7:c0853d40 r6:c0858b4c r5:00000089
[ 91.758373] r4:00000000
[ 91.758386] [<c0046df4>] (irq_exit) from [<c00821c8>] (__handle_domain_irq+0xa0/0x12c)
[ 91.758395] r4:00000000 r3:00000100
[ 91.758406] [<c0082128>] (__handle_domain_irq) from [<c00094e0>] (gic_handle_irq+0x28/0x68)
[ 91.758426] r10:c08e3510 r9:20000013 r8:fa240100 r7:c0858d70 r6:ee441bb8 r5:00000039
[ 91.758433] r4:fa24010c
[ 91.758445] [<c00094b8>] (gic_handle_irq) from [<c05fd540>] (__irq_svc+0x40/0x74)
[ 91.758450] Exception stack(0xee441bb8 to 0xee441c00)
[ 91.758457] 1ba0: 00000000 00000001
[ 91.758468] 1bc0: 00000000 ee440000 c08e2524 0000004d 00000274 00000000 00000000 20000013
[ 91.758479] 1be0: c08e3510 ee441c4c ee441b60 ee441c00 c03acfec c0080d4c 60000013 ffffffff
[ 91.758499] r8:00000000 r7:ee441bec r6:ffffffff r5:60000013 r4:c0080d4c r3:c03acfec
[ 91.758524] [<c0080950>] (console_unlock) from [<c0081670>] (vprintk_emit+0x20c/0x500)
[ 91.758544] r10:ee441cc0 r9:c08d3550 r8:c08e3ea0 r7:00000000 r6:00000001 r5:0000003d
[ 91.758551] r4:c08d3550
[ 91.758573] [<c0081464>] (vprintk_emit) from [<c03f6f70>] (dev_vprintk_emit+0x104/0x1ac)
[ 91.758593] r10:ee441d8c r9:0000000e r8:c07951e0 r7:00000006 r6:ee441cc0 r5:0000000d
[ 91.758599] r4:ee731068
[ 91.758612] [<c03f6e6c>] (dev_vprintk_emit) from [<c03f7040>] (dev_printk_emit+0x28/0x30)
[ 91.758632] r10:00000001 r9:ee5f8410 r8:ee731000 r7:ed429000 r6:00000006 r5:ee441dc0
[ 91.758638] r4:ee731068
[ 91.758651] [<c03f701c>] (dev_printk_emit) from [<c03f7098>] (__dev_printk+0x50/0x70)
[ 91.758660] r3:bf2268cc r2:c07951e0
[ 91.758673] [<c03f7048>] (__dev_printk) from [<c03f70f4>] (_dev_info+0x3c/0x48)
[ 91.758686] r6:00000000 r5:ee731068 r4:ee731000
[ 91.758790] [<c03f70bc>] (_dev_info) from [<bf20ec3c>] (usb_new_device+0x11c/0x518 [usbcore])
[ 91.758804] r3:00000003 r2:00001d6b r1:bf225bc4
[ 91.758881] [<bf20eb20>] (usb_new_device [usbcore]) from [<bf213560>] (usb_otg_add_hcd+0x514/0x7f8 [usbcore])
[ 91.758903] r10:00000001 r9:ee5f8410 r8:ee731000 r7:000000fe r6:ed4290c8 r5:00000000
[ 91.758909] r4:ed429000
[ 91.758957] [<bf21304c>] (usb_otg_add_hcd [usbcore]) from [<c047a238>] (usb_otg_start_host+0xb8/0xf8)
[ 91.758978] r10:00000000 r9:00000002 r8:00000000 r7:ee02b000 r6:ee452808 r5:ee452808
[ 91.758985] r4:ee452808
[ 91.758997] [<c047a180>] (usb_otg_start_host) from [<c047a020>] (drd_set_protocol+0xac/0xd8)
[ 91.759007] r4:00000001 r3:c047a180
[ 91.759018] [<c0479f74>] (drd_set_protocol) from [<c047a2ec>] (drd_set_state+0x74/0x98)
[ 91.759027] r5:ee452808 r4:00000009
[ 91.759039] [<c047a278>] (drd_set_state) from [<c047a3dc>] (usb_otg_work+0xcc/0x154)
[ 91.759054] r6:ee452808 r5:ee4528b8 r4:ee452968 r3:00000000
[ 91.759072] [<c047a310>] (usb_otg_work) from [<c005754c>] (process_one_work+0x128/0x340)
[ 91.759087] r6:ee02ac00 r5:ee452968 r4:ee42b900 r3:c047a310
[ 91.759100] [<c0057424>] (process_one_work) from [<c00578f8>] (worker_thread+0x158/0x49c)
[ 91.759120] r10:ee42b900 r9:00000002 r8:ee02ac00 r7:00000088 r6:ee42b918 r5:ee02ac00
[ 91.759127] r4:ee02ac14
[ 91.759145] [<c00577a0>] (worker_thread) from [<c005cc40>] (kthread+0xdc/0xf8)
[ 91.759165] r10:00000000 r9:00000000 r8:00000000 r7:c00577a0 r6:ee42b900 r5:ee429940
[ 91.759174] r4:00000000 r3:00000000
[ 91.759190] [<c005cb64>] (kthread) from [<c000fc08>] (ret_from_fork+0x14/0x2c)
[ 91.759206] r7:00000000 r6:00000000 r5:c005cb64 r4:ee429940
[ 91.759209] handlers:
[ 91.759255] [<bf211b5c>] usb_hcd_irq [usbcore]
[ 91.759260] Disabling IRQ #254

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

diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index 2ca2cef..6b1930d 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -2706,6 +2706,7 @@ int usb_add_hcd(struct usb_hcd *hcd,
int retval;
struct usb_device *rhdev;

+ hcd->flags = 0;
if (IS_ENABLED(CONFIG_USB_PHY) && !hcd->usb_phy) {
struct usb_phy *phy = usb_get_phy_dev(hcd->self.controller, 0);

--
2.5.0

2016-04-05 14:06:01

by Roger Quadros

[permalink] [raw]
Subject: [PATCH v6 04/12] usb: otg-fsm: move host controller operations into usb_otg->hcd_ops

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

Signed-off-by: Roger Quadros <[email protected]>
---
drivers/usb/chipidea/otg_fsm.c | 7 +++++++
drivers/usb/common/usb-otg-fsm.c | 15 +++++++++++----
drivers/usb/phy/phy-fsl-usb.c | 7 +++++++
include/linux/usb/otg.h | 2 ++
4 files changed, 27 insertions(+), 4 deletions(-)

diff --git a/drivers/usb/chipidea/otg_fsm.c b/drivers/usb/chipidea/otg_fsm.c
index f4e9fb5..5fdf8ca 100644
--- a/drivers/usb/chipidea/otg_fsm.c
+++ b/drivers/usb/chipidea/otg_fsm.c
@@ -582,6 +582,12 @@ static struct otg_fsm_ops ci_otg_ops = {
.start_gadget = ci_otg_start_gadget,
};

+static struct otg_hcd_ops ci_hcd_ops = {
+ .usb_bus_start_enum = usb_bus_start_enum,
+ .usb_control_msg = usb_control_msg,
+ .usb_hub_find_child = usb_hub_find_child,
+};
+
int ci_otg_fsm_work(struct ci_hdrc *ci)
{
/*
@@ -805,6 +811,7 @@ int ci_hdrc_otg_fsm_init(struct ci_hdrc *ci)
ci->otg.state = OTG_STATE_UNDEFINED;
ci->otg.fsm.ops = &ci_otg_ops;
ci->otg.fsm.dev = ci->dev;
+ ci->otg.hcd_ops = &ci_hcd_ops;
ci->gadget.hnp_polling_support = 1;
ci->otg.fsm.host_req_flag = devm_kzalloc(ci->dev, 1, GFP_KERNEL);
if (!ci->otg.fsm.host_req_flag)
diff --git a/drivers/usb/common/usb-otg-fsm.c b/drivers/usb/common/usb-otg-fsm.c
index abc462c..2cb4aed 100644
--- a/drivers/usb/common/usb-otg-fsm.c
+++ b/drivers/usb/common/usb-otg-fsm.c
@@ -135,11 +135,16 @@ static void otg_hnp_polling_work(struct work_struct *work)
enum usb_otg_state state = otg->state;
u8 flag;
int retval;
+ struct otg_hcd_ops *hcd_ops = otg->hcd_ops;

if (state != OTG_STATE_A_HOST && state != OTG_STATE_B_HOST)
return;

- udev = usb_hub_find_child(otg->host->root_hub, 1);
+ if (!hcd_ops || !hcd_ops->usb_control_msg ||
+ !hcd_ops->usb_hub_find_child)
+ return;
+
+ udev = hcd_ops->usb_hub_find_child(otg->host->root_hub, 1);
if (!udev) {
dev_err(otg->host->controller,
"no usb dev connected, can't start HNP polling\n");
@@ -148,7 +153,7 @@ static void otg_hnp_polling_work(struct work_struct *work)

*fsm->host_req_flag = 0;
/* Get host request flag from connected USB device */
- retval = usb_control_msg(udev,
+ retval = hcd_ops->usb_control_msg(udev,
usb_rcvctrlpipe(udev, 0),
USB_REQ_GET_STATUS,
USB_DIR_IN | USB_RECIP_DEVICE,
@@ -177,7 +182,7 @@ static void otg_hnp_polling_work(struct work_struct *work)
if (state == OTG_STATE_A_HOST) {
/* Set b_hnp_enable */
if (!otg->host->b_hnp_enable) {
- retval = usb_control_msg(udev,
+ retval = hcd_ops->usb_control_msg(udev,
usb_sndctrlpipe(udev, 0),
USB_REQ_SET_FEATURE, 0,
USB_DEVICE_B_HNP_ENABLE,
@@ -256,7 +261,9 @@ static int otg_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state)
otg_loc_conn(otg, 0);
otg_loc_sof(otg, 1);
otg_set_protocol(fsm, PROTO_HOST);
- usb_bus_start_enum(otg->host, otg->host->otg_port);
+ if (otg->hcd_ops && otg->hcd_ops->usb_bus_start_enum)
+ otg->hcd_ops->usb_bus_start_enum(otg->host,
+ otg->host->otg_port);
otg_start_hnp_polling(fsm);
break;
case OTG_STATE_A_IDLE:
diff --git a/drivers/usb/phy/phy-fsl-usb.c b/drivers/usb/phy/phy-fsl-usb.c
index a18a2ee..39605d5 100644
--- a/drivers/usb/phy/phy-fsl-usb.c
+++ b/drivers/usb/phy/phy-fsl-usb.c
@@ -792,6 +792,12 @@ static struct otg_fsm_ops fsl_otg_ops = {
.start_gadget = fsl_otg_start_gadget,
};

+static struct otg_hcd_ops fsl_hcd_ops = {
+ .usb_bus_start_enum = usb_bus_start_enum,
+ .usb_control_msg = usb_control_msg,
+ .usb_hub_find_child = usb_hub_find_child,
+};
+
/* Initialize the global variable fsl_otg_dev and request IRQ for OTG */
static int fsl_otg_conf(struct platform_device *pdev)
{
@@ -821,6 +827,7 @@ static int fsl_otg_conf(struct platform_device *pdev)
/* Set OTG state machine operations */
fsl_otg_tc->otg.fsm.ops = &fsl_otg_ops;
fsl_otg_tc->otg.fsm.dev = &pdev->dev;
+ fsl_otg_tc->otg.hcd_ops = &fsl_hcd_ops;

/* initialize the otg structure */
fsl_otg_tc->phy.label = DRIVER_DESC;
diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h
index e8a14dc..85b8fb5 100644
--- a/include/linux/usb/otg.h
+++ b/include/linux/usb/otg.h
@@ -12,6 +12,7 @@
#include <linux/phy/phy.h>
#include <linux/usb/phy.h>
#include <linux/usb/otg-fsm.h>
+#include <linux/usb/hcd.h>

struct usb_otg {
u8 default_a;
@@ -24,6 +25,7 @@ struct usb_otg {

enum usb_otg_state state;
struct otg_fsm fsm;
+ struct otg_hcd_ops *hcd_ops;

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

2016-04-05 14:06:21

by Roger Quadros

[permalink] [raw]
Subject: [PATCH v6 03/12] usb: otg-fsm: use usb_otg wherever possible

Move otg_fsm into usb_otg and use usb_otg wherever possible
in the usb_otg APIs.

Signed-off-by: Roger Quadros <[email protected]>
---
drivers/usb/chipidea/ci.h | 1 -
drivers/usb/chipidea/core.c | 12 +--
drivers/usb/chipidea/debug.c | 2 +-
drivers/usb/chipidea/otg_fsm.c | 171 ++++++++++++++++++-------------------
drivers/usb/chipidea/udc.c | 17 ++--
drivers/usb/common/usb-otg-fsm.c | 180 ++++++++++++++++++++-------------------
drivers/usb/phy/phy-fsl-usb.c | 143 +++++++++++++++----------------
drivers/usb/phy/phy-fsl-usb.h | 3 +-
include/linux/usb/otg-fsm.h | 132 +++-------------------------
include/linux/usb/otg.h | 107 +++++++++++++++++++++++
10 files changed, 384 insertions(+), 384 deletions(-)

diff --git a/drivers/usb/chipidea/ci.h b/drivers/usb/chipidea/ci.h
index cd41455..c523975 100644
--- a/drivers/usb/chipidea/ci.h
+++ b/drivers/usb/chipidea/ci.h
@@ -209,7 +209,6 @@ struct ci_hdrc {
enum ci_role role;
bool is_otg;
struct usb_otg otg;
- struct otg_fsm fsm;
struct hrtimer otg_fsm_hrtimer;
ktime_t hr_timeouts[NUM_OTG_FSM_TIMERS];
unsigned enabled_otg_timer_bits;
diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c
index 69426e6..a5570a9 100644
--- a/drivers/usb/chipidea/core.c
+++ b/drivers/usb/chipidea/core.c
@@ -1085,7 +1085,7 @@ static int ci_hdrc_remove(struct platform_device *pdev)
/* Prepare wakeup by SRP before suspend */
static void ci_otg_fsm_suspend_for_srp(struct ci_hdrc *ci)
{
- if ((ci->fsm.otg->state == OTG_STATE_A_IDLE) &&
+ if ((ci->otg.state == OTG_STATE_A_IDLE) &&
!hw_read_otgsc(ci, OTGSC_ID)) {
hw_write(ci, OP_PORTSC, PORTSC_W1C_BITS | PORTSC_PP,
PORTSC_PP);
@@ -1097,13 +1097,13 @@ static void ci_otg_fsm_suspend_for_srp(struct ci_hdrc *ci)
/* Handle SRP when wakeup by data pulse */
static void ci_otg_fsm_wakeup_by_srp(struct ci_hdrc *ci)
{
- if ((ci->fsm.otg->state == OTG_STATE_A_IDLE) &&
- (ci->fsm.a_bus_drop == 1) && (ci->fsm.a_bus_req == 0)) {
+ if ((ci->otg.state == OTG_STATE_A_IDLE) &&
+ (ci->otg.fsm.a_bus_drop == 1) && (ci->otg.fsm.a_bus_req == 0)) {
if (!hw_read_otgsc(ci, OTGSC_ID)) {
- ci->fsm.a_srp_det = 1;
- ci->fsm.a_bus_drop = 0;
+ ci->otg.fsm.a_srp_det = 1;
+ ci->otg.fsm.a_bus_drop = 0;
} else {
- ci->fsm.id = 1;
+ ci->otg.fsm.id = 1;
}
ci_otg_queue_work(ci);
}
diff --git a/drivers/usb/chipidea/debug.c b/drivers/usb/chipidea/debug.c
index 6d23eed..374cdaa 100644
--- a/drivers/usb/chipidea/debug.c
+++ b/drivers/usb/chipidea/debug.c
@@ -224,7 +224,7 @@ static int ci_otg_show(struct seq_file *s, void *unused)
if (!ci || !ci_otg_is_fsm_mode(ci))
return 0;

- fsm = &ci->fsm;
+ fsm = &ci->otg.fsm;

/* ------ State ----- */
seq_printf(s, "OTG state: %s\n\n",
diff --git a/drivers/usb/chipidea/otg_fsm.c b/drivers/usb/chipidea/otg_fsm.c
index 5f169b3..f4e9fb5 100644
--- a/drivers/usb/chipidea/otg_fsm.c
+++ b/drivers/usb/chipidea/otg_fsm.c
@@ -40,7 +40,7 @@ get_a_bus_req(struct device *dev, struct device_attribute *attr, char *buf)

next = buf;
size = PAGE_SIZE;
- t = scnprintf(next, size, "%d\n", ci->fsm.a_bus_req);
+ t = scnprintf(next, size, "%d\n", ci->otg.fsm.a_bus_req);
size -= t;
next += t;

@@ -56,25 +56,25 @@ set_a_bus_req(struct device *dev, struct device_attribute *attr,
if (count > 2)
return -1;

- mutex_lock(&ci->fsm.lock);
+ mutex_lock(&ci->otg.fsm.lock);
if (buf[0] == '0') {
- ci->fsm.a_bus_req = 0;
+ ci->otg.fsm.a_bus_req = 0;
} else if (buf[0] == '1') {
/* If a_bus_drop is TRUE, a_bus_req can't be set */
- if (ci->fsm.a_bus_drop) {
- mutex_unlock(&ci->fsm.lock);
+ if (ci->otg.fsm.a_bus_drop) {
+ mutex_unlock(&ci->otg.fsm.lock);
return count;
}
- ci->fsm.a_bus_req = 1;
- if (ci->fsm.otg->state == OTG_STATE_A_PERIPHERAL) {
+ ci->otg.fsm.a_bus_req = 1;
+ if (ci->otg.state == OTG_STATE_A_PERIPHERAL) {
ci->gadget.host_request_flag = 1;
- mutex_unlock(&ci->fsm.lock);
+ mutex_unlock(&ci->otg.fsm.lock);
return count;
}
}

ci_otg_queue_work(ci);
- mutex_unlock(&ci->fsm.lock);
+ mutex_unlock(&ci->otg.fsm.lock);

return count;
}
@@ -89,7 +89,7 @@ get_a_bus_drop(struct device *dev, struct device_attribute *attr, char *buf)

next = buf;
size = PAGE_SIZE;
- t = scnprintf(next, size, "%d\n", ci->fsm.a_bus_drop);
+ t = scnprintf(next, size, "%d\n", ci->otg.fsm.a_bus_drop);
size -= t;
next += t;

@@ -105,16 +105,16 @@ set_a_bus_drop(struct device *dev, struct device_attribute *attr,
if (count > 2)
return -1;

- mutex_lock(&ci->fsm.lock);
+ mutex_lock(&ci->otg.fsm.lock);
if (buf[0] == '0') {
- ci->fsm.a_bus_drop = 0;
+ ci->otg.fsm.a_bus_drop = 0;
} else if (buf[0] == '1') {
- ci->fsm.a_bus_drop = 1;
- ci->fsm.a_bus_req = 0;
+ ci->otg.fsm.a_bus_drop = 1;
+ ci->otg.fsm.a_bus_req = 0;
}

ci_otg_queue_work(ci);
- mutex_unlock(&ci->fsm.lock);
+ mutex_unlock(&ci->otg.fsm.lock);

return count;
}
@@ -130,7 +130,7 @@ get_b_bus_req(struct device *dev, struct device_attribute *attr, char *buf)

next = buf;
size = PAGE_SIZE;
- t = scnprintf(next, size, "%d\n", ci->fsm.b_bus_req);
+ t = scnprintf(next, size, "%d\n", ci->otg.fsm.b_bus_req);
size -= t;
next += t;

@@ -146,20 +146,20 @@ set_b_bus_req(struct device *dev, struct device_attribute *attr,
if (count > 2)
return -1;

- mutex_lock(&ci->fsm.lock);
+ mutex_lock(&ci->otg.fsm.lock);
if (buf[0] == '0')
- ci->fsm.b_bus_req = 0;
+ ci->otg.fsm.b_bus_req = 0;
else if (buf[0] == '1') {
- ci->fsm.b_bus_req = 1;
- if (ci->fsm.otg->state == OTG_STATE_B_PERIPHERAL) {
+ ci->otg.fsm.b_bus_req = 1;
+ if (ci->otg.state == OTG_STATE_B_PERIPHERAL) {
ci->gadget.host_request_flag = 1;
- mutex_unlock(&ci->fsm.lock);
+ mutex_unlock(&ci->otg.fsm.lock);
return count;
}
}

ci_otg_queue_work(ci);
- mutex_unlock(&ci->fsm.lock);
+ mutex_unlock(&ci->otg.fsm.lock);

return count;
}
@@ -174,12 +174,12 @@ set_a_clr_err(struct device *dev, struct device_attribute *attr,
if (count > 2)
return -1;

- mutex_lock(&ci->fsm.lock);
+ mutex_lock(&ci->otg.fsm.lock);
if (buf[0] == '1')
- ci->fsm.a_clr_err = 1;
+ ci->otg.fsm.a_clr_err = 1;

ci_otg_queue_work(ci);
- mutex_unlock(&ci->fsm.lock);
+ mutex_unlock(&ci->otg.fsm.lock);

return count;
}
@@ -287,64 +287,64 @@ static void ci_otg_del_timer(struct ci_hdrc *ci, enum otg_fsm_timer t)
/* OTG FSM timer handlers */
static int a_wait_vrise_tmout(struct ci_hdrc *ci)
{
- ci->fsm.a_wait_vrise_tmout = 1;
+ ci->otg.fsm.a_wait_vrise_tmout = 1;
return 0;
}

static int a_wait_vfall_tmout(struct ci_hdrc *ci)
{
- ci->fsm.a_wait_vfall_tmout = 1;
+ ci->otg.fsm.a_wait_vfall_tmout = 1;
return 0;
}

static int a_wait_bcon_tmout(struct ci_hdrc *ci)
{
- ci->fsm.a_wait_bcon_tmout = 1;
+ ci->otg.fsm.a_wait_bcon_tmout = 1;
return 0;
}

static int a_aidl_bdis_tmout(struct ci_hdrc *ci)
{
- ci->fsm.a_aidl_bdis_tmout = 1;
+ ci->otg.fsm.a_aidl_bdis_tmout = 1;
return 0;
}

static int b_ase0_brst_tmout(struct ci_hdrc *ci)
{
- ci->fsm.b_ase0_brst_tmout = 1;
+ ci->otg.fsm.b_ase0_brst_tmout = 1;
return 0;
}

static int a_bidl_adis_tmout(struct ci_hdrc *ci)
{
- ci->fsm.a_bidl_adis_tmout = 1;
+ ci->otg.fsm.a_bidl_adis_tmout = 1;
return 0;
}

static int b_aidl_bdis_tmout(struct ci_hdrc *ci)
{
- ci->fsm.a_bus_suspend = 1;
+ ci->otg.fsm.a_bus_suspend = 1;
return 0;
}

static int b_se0_srp_tmout(struct ci_hdrc *ci)
{
- ci->fsm.b_se0_srp = 1;
+ ci->otg.fsm.b_se0_srp = 1;
return 0;
}

static int b_srp_fail_tmout(struct ci_hdrc *ci)
{
- ci->fsm.b_srp_done = 1;
+ ci->otg.fsm.b_srp_done = 1;
return 1;
}

static int b_data_pls_tmout(struct ci_hdrc *ci)
{
- ci->fsm.b_srp_done = 1;
- ci->fsm.b_bus_req = 0;
- if (ci->fsm.power_up)
- ci->fsm.power_up = 0;
+ ci->otg.fsm.b_srp_done = 1;
+ ci->otg.fsm.b_bus_req = 0;
+ if (ci->otg.fsm.power_up)
+ ci->otg.fsm.power_up = 0;
hw_write_otgsc(ci, OTGSC_HABA, 0);
pm_runtime_put(ci->dev);
return 0;
@@ -352,9 +352,9 @@ static int b_data_pls_tmout(struct ci_hdrc *ci)

static int b_ssend_srp_tmout(struct ci_hdrc *ci)
{
- ci->fsm.b_ssend_srp = 1;
+ ci->otg.fsm.b_ssend_srp = 1;
/* only vbus fall below B_sess_vld in b_idle state */
- if (ci->fsm.otg->state == OTG_STATE_B_IDLE)
+ if (ci->otg.state == OTG_STATE_B_IDLE)
return 0;
else
return 1;
@@ -435,18 +435,18 @@ static int ci_otg_init_timers(struct ci_hdrc *ci)
/* -------------------------------------------------------------*/
/* Operations that will be called from OTG Finite State Machine */
/* -------------------------------------------------------------*/
-static void ci_otg_fsm_add_timer(struct otg_fsm *fsm, enum otg_fsm_timer t)
+static void ci_otg_fsm_add_timer(struct usb_otg *otg, enum otg_fsm_timer t)
{
- struct ci_hdrc *ci = container_of(fsm, struct ci_hdrc, fsm);
+ struct ci_hdrc *ci = container_of(otg, struct ci_hdrc, otg);

if (t < NUM_OTG_FSM_TIMERS)
ci_otg_add_timer(ci, t);
return;
}

-static void ci_otg_fsm_del_timer(struct otg_fsm *fsm, enum otg_fsm_timer t)
+static void ci_otg_fsm_del_timer(struct usb_otg *otg, enum otg_fsm_timer t)
{
- struct ci_hdrc *ci = container_of(fsm, struct ci_hdrc, fsm);
+ struct ci_hdrc *ci = container_of(otg, struct ci_hdrc, otg);

if (t < NUM_OTG_FSM_TIMERS)
ci_otg_del_timer(ci, t);
@@ -457,10 +457,10 @@ static void ci_otg_fsm_del_timer(struct otg_fsm *fsm, enum otg_fsm_timer t)
* A-device drive vbus: turn on vbus regulator and enable port power
* Data pulse irq should be disabled while vbus is on.
*/
-static void ci_otg_drv_vbus(struct otg_fsm *fsm, int on)
+static void ci_otg_drv_vbus(struct usb_otg *otg, int on)
{
int ret;
- struct ci_hdrc *ci = container_of(fsm, struct ci_hdrc, fsm);
+ struct ci_hdrc *ci = container_of(otg, struct ci_hdrc, otg);

if (on) {
/* Enable power power */
@@ -478,23 +478,23 @@ static void ci_otg_drv_vbus(struct otg_fsm *fsm, int on)
/* Disable data pulse irq */
hw_write_otgsc(ci, OTGSC_DPIE, 0);

- fsm->a_srp_det = 0;
- fsm->power_up = 0;
+ otg->fsm.a_srp_det = 0;
+ otg->fsm.power_up = 0;
} else {
if (ci->platdata->reg_vbus)
regulator_disable(ci->platdata->reg_vbus);

- fsm->a_bus_drop = 1;
- fsm->a_bus_req = 0;
+ otg->fsm.a_bus_drop = 1;
+ otg->fsm.a_bus_req = 0;
}
}

/*
* Control data line by Run Stop bit.
*/
-static void ci_otg_loc_conn(struct otg_fsm *fsm, int on)
+static void ci_otg_loc_conn(struct usb_otg *otg, int on)
{
- struct ci_hdrc *ci = container_of(fsm, struct ci_hdrc, fsm);
+ struct ci_hdrc *ci = container_of(otg, struct ci_hdrc, otg);

if (on)
hw_write(ci, OP_USBCMD, USBCMD_RS, USBCMD_RS);
@@ -511,14 +511,14 @@ static void ci_otg_loc_conn(struct otg_fsm *fsm, int on)
* so the usb device class driver need support autosuspend,
* otherwise the bus suspend will not happen.
*/
-static void ci_otg_loc_sof(struct otg_fsm *fsm, int on)
+static void ci_otg_loc_sof(struct usb_otg *otg, int on)
{
struct usb_device *udev;

- if (!fsm->otg->host)
+ if (!otg->host)
return;

- udev = usb_hub_find_child(fsm->otg->host->root_hub, 1);
+ udev = usb_hub_find_child(otg->host->root_hub, 1);
if (!udev)
return;

@@ -534,9 +534,9 @@ static void ci_otg_loc_sof(struct otg_fsm *fsm, int on)
* Start SRP pulsing by data-line pulsing,
* no v-bus pulsing followed
*/
-static void ci_otg_start_pulse(struct otg_fsm *fsm)
+static void ci_otg_start_pulse(struct usb_otg *otg)
{
- struct ci_hdrc *ci = container_of(fsm, struct ci_hdrc, fsm);
+ struct ci_hdrc *ci = container_of(otg, struct ci_hdrc, otg);

/* Hardware Assistant Data pulse */
hw_write_otgsc(ci, OTGSC_HADP, OTGSC_HADP);
@@ -545,9 +545,9 @@ static void ci_otg_start_pulse(struct otg_fsm *fsm)
ci_otg_add_timer(ci, B_DATA_PLS);
}

-static int ci_otg_start_host(struct otg_fsm *fsm, int on)
+static int ci_otg_start_host(struct usb_otg *otg, int on)
{
- struct ci_hdrc *ci = container_of(fsm, struct ci_hdrc, fsm);
+ struct ci_hdrc *ci = container_of(otg, struct ci_hdrc, otg);

if (on) {
ci_role_stop(ci);
@@ -559,9 +559,9 @@ static int ci_otg_start_host(struct otg_fsm *fsm, int on)
return 0;
}

-static int ci_otg_start_gadget(struct otg_fsm *fsm, int on)
+static int ci_otg_start_gadget(struct usb_otg *otg, int on)
{
- struct ci_hdrc *ci = container_of(fsm, struct ci_hdrc, fsm);
+ struct ci_hdrc *ci = container_of(otg, struct ci_hdrc, otg);

if (on)
usb_gadget_vbus_connect(&ci->gadget);
@@ -588,13 +588,13 @@ int ci_otg_fsm_work(struct ci_hdrc *ci)
* Don't do fsm transition for B device
* when there is no gadget class driver
*/
- if (ci->fsm.id && !(ci->driver) &&
- ci->fsm.otg->state < OTG_STATE_A_IDLE)
+ if (ci->otg.fsm.id && !(ci->driver) &&
+ ci->otg.state < OTG_STATE_A_IDLE)
return 0;

pm_runtime_get_sync(ci->dev);
- if (otg_statemachine(&ci->fsm)) {
- if (ci->fsm.otg->state == OTG_STATE_A_IDLE) {
+ if (otg_statemachine(&ci->otg)) {
+ if (ci->otg.state == OTG_STATE_A_IDLE) {
/*
* Further state change for cases:
* a_idle to b_idle; or
@@ -603,8 +603,8 @@ int ci_otg_fsm_work(struct ci_hdrc *ci)
* consequently; or
* a_idle to a_wait_vrise when power up
*/
- if ((ci->fsm.id) || (ci->id_event) ||
- (ci->fsm.power_up)) {
+ if ((ci->otg.fsm.id) || (ci->id_event) ||
+ (ci->otg.fsm.power_up)) {
ci_otg_queue_work(ci);
} else {
/* Enable data pulse irq */
@@ -615,16 +615,16 @@ int ci_otg_fsm_work(struct ci_hdrc *ci)
}
if (ci->id_event)
ci->id_event = false;
- } else if (ci->fsm.otg->state == OTG_STATE_B_IDLE) {
- if (ci->fsm.b_sess_vld) {
- ci->fsm.power_up = 0;
+ } else if (ci->otg.state == OTG_STATE_B_IDLE) {
+ if (ci->otg.fsm.b_sess_vld) {
+ ci->otg.fsm.power_up = 0;
/*
* Further transite to b_periphearl state
* when register gadget driver with vbus on
*/
ci_otg_queue_work(ci);
}
- } else if (ci->fsm.otg->state == OTG_STATE_A_HOST) {
+ } else if (ci->otg.state == OTG_STATE_A_HOST) {
pm_runtime_mark_last_busy(ci->dev);
pm_runtime_put_autosuspend(ci->dev);
return 0;
@@ -641,13 +641,13 @@ int ci_otg_fsm_work(struct ci_hdrc *ci)
static void ci_otg_fsm_event(struct ci_hdrc *ci)
{
u32 intr_sts, otg_bsess_vld, port_conn;
- struct otg_fsm *fsm = &ci->fsm;
+ struct otg_fsm *fsm = &ci->otg.fsm;

intr_sts = hw_read_intr_status(ci);
otg_bsess_vld = hw_read_otgsc(ci, OTGSC_BSV);
port_conn = hw_read(ci, OP_PORTSC, PORTSC_CCS);

- switch (ci->fsm.otg->state) {
+ switch (ci->otg.state) {
case OTG_STATE_A_WAIT_BCON:
if (port_conn) {
fsm->b_conn = 1;
@@ -737,7 +737,7 @@ irqreturn_t ci_otg_fsm_irq(struct ci_hdrc *ci)
{
irqreturn_t retval = IRQ_NONE;
u32 otgsc, otg_int_src = 0;
- struct otg_fsm *fsm = &ci->fsm;
+ struct otg_fsm *fsm = &ci->otg.fsm;

otgsc = hw_read_otgsc(ci, ~0);
otg_int_src = otgsc & OTGSC_INT_STATUS_BITS & (otgsc >> 8);
@@ -800,18 +800,17 @@ int ci_hdrc_otg_fsm_init(struct ci_hdrc *ci)
ci->otg.usb_phy = ci->usb_phy;

ci->otg.gadget = &ci->gadget;
- ci->fsm.otg = &ci->otg;
- ci->fsm.power_up = 1;
- ci->fsm.id = hw_read_otgsc(ci, OTGSC_ID) ? 1 : 0;
- ci->fsm.otg->state = OTG_STATE_UNDEFINED;
- ci->fsm.ops = &ci_otg_ops;
- ci->fsm.dev = ci->dev;
+ ci->otg.fsm.power_up = 1;
+ ci->otg.fsm.id = hw_read_otgsc(ci, OTGSC_ID) ? 1 : 0;
+ ci->otg.state = OTG_STATE_UNDEFINED;
+ ci->otg.fsm.ops = &ci_otg_ops;
+ ci->otg.fsm.dev = ci->dev;
ci->gadget.hnp_polling_support = 1;
- ci->fsm.host_req_flag = devm_kzalloc(ci->dev, 1, GFP_KERNEL);
- if (!ci->fsm.host_req_flag)
+ ci->otg.fsm.host_req_flag = devm_kzalloc(ci->dev, 1, GFP_KERNEL);
+ if (!ci->otg.fsm.host_req_flag)
return -ENOMEM;

- mutex_init(&ci->fsm.lock);
+ mutex_init(&ci->otg.fsm.lock);

retval = ci_otg_init_timers(ci);
if (retval) {
@@ -831,10 +830,10 @@ int ci_hdrc_otg_fsm_init(struct ci_hdrc *ci)
/* Enable A vbus valid irq */
hw_write_otgsc(ci, OTGSC_AVVIE, OTGSC_AVVIE);

- if (ci->fsm.id) {
- ci->fsm.b_ssend_srp =
+ if (ci->otg.fsm.id) {
+ ci->otg.fsm.b_ssend_srp =
hw_read_otgsc(ci, OTGSC_BSV) ? 0 : 1;
- ci->fsm.b_sess_vld =
+ ci->otg.fsm.b_sess_vld =
hw_read_otgsc(ci, OTGSC_BSV) ? 1 : 0;
/* Enable BSV irq */
hw_write_otgsc(ci, OTGSC_BSVIE, OTGSC_BSVIE);
diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c
index 065f5d9..7a6b0b5 100644
--- a/drivers/usb/chipidea/udc.c
+++ b/drivers/usb/chipidea/udc.c
@@ -20,6 +20,7 @@
#include <linux/pm_runtime.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
+#include <linux/usb/otg.h>
#include <linux/usb/otg-fsm.h>
#include <linux/usb/chipidea.h>

@@ -1739,7 +1740,7 @@ static int ci_udc_start(struct usb_gadget *gadget,
ci->driver = driver;

/* Start otg fsm for B-device */
- if (ci_otg_is_fsm_mode(ci) && ci->fsm.id) {
+ if (ci_otg_is_fsm_mode(ci) && ci->otg.fsm.id) {
ci_hdrc_otg_fsm_start(ci);
return retval;
}
@@ -1767,15 +1768,15 @@ static void ci_udc_stop_for_otg_fsm(struct ci_hdrc *ci)
if (!ci_otg_is_fsm_mode(ci))
return;

- mutex_lock(&ci->fsm.lock);
- if (ci->fsm.otg->state == OTG_STATE_A_PERIPHERAL) {
- ci->fsm.a_bidl_adis_tmout = 1;
+ mutex_lock(&ci->otg.fsm.lock);
+ if (ci->otg.state == OTG_STATE_A_PERIPHERAL) {
+ ci->otg.fsm.a_bidl_adis_tmout = 1;
ci_hdrc_otg_fsm_start(ci);
- } else if (ci->fsm.otg->state == OTG_STATE_B_PERIPHERAL) {
- ci->fsm.protocol = PROTO_UNDEF;
- ci->fsm.otg->state = OTG_STATE_UNDEFINED;
+ } else if (ci->otg.state == OTG_STATE_B_PERIPHERAL) {
+ ci->otg.fsm.protocol = PROTO_UNDEF;
+ ci->otg.state = OTG_STATE_UNDEFINED;
}
- mutex_unlock(&ci->fsm.lock);
+ mutex_unlock(&ci->otg.fsm.lock);
}

/**
diff --git a/drivers/usb/common/usb-otg-fsm.c b/drivers/usb/common/usb-otg-fsm.c
index c5a61fe..abc462c 100644
--- a/drivers/usb/common/usb-otg-fsm.c
+++ b/drivers/usb/common/usb-otg-fsm.c
@@ -33,6 +33,7 @@
/* Change USB protocol when there is a protocol change */
static int otg_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) {
@@ -41,17 +42,17 @@ static int otg_set_protocol(struct otg_fsm *fsm, int protocol)
fsm->protocol, protocol);
/* stop old protocol */
if (fsm->protocol == PROTO_HOST)
- ret = otg_start_host(fsm, 0);
+ ret = otg_start_host(otg, 0);
else if (fsm->protocol == PROTO_GADGET)
- ret = otg_start_gadget(fsm, 0);
+ ret = otg_start_gadget(otg, 0);
if (ret)
return ret;

/* start new protocol */
if (protocol == PROTO_HOST)
- ret = otg_start_host(fsm, 1);
+ ret = otg_start_host(otg, 1);
else if (protocol == PROTO_GADGET)
- ret = otg_start_gadget(fsm, 1);
+ ret = otg_start_gadget(otg, 1);
if (ret)
return ret;

@@ -65,9 +66,11 @@ static int otg_set_protocol(struct otg_fsm *fsm, int protocol)
/* 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)
{
+ struct usb_otg *otg = container_of(fsm, struct usb_otg, fsm);
+
switch (old_state) {
case OTG_STATE_B_IDLE:
- otg_del_timer(fsm, B_SE0_SRP);
+ otg_del_timer(otg, B_SE0_SRP);
fsm->b_se0_srp = 0;
fsm->adp_sns = 0;
fsm->adp_prb = 0;
@@ -77,11 +80,11 @@ static void otg_leave_state(struct otg_fsm *fsm, enum usb_otg_state old_state)
fsm->b_srp_done = 0;
break;
case OTG_STATE_B_PERIPHERAL:
- if (fsm->otg->gadget)
- fsm->otg->gadget->host_request_flag = 0;
+ if (otg->gadget)
+ otg->gadget->host_request_flag = 0;
break;
case OTG_STATE_B_WAIT_ACON:
- otg_del_timer(fsm, B_ASE0_BRST);
+ otg_del_timer(otg, B_ASE0_BRST);
fsm->b_ase0_brst_tmout = 0;
break;
case OTG_STATE_B_HOST:
@@ -90,31 +93,31 @@ static void otg_leave_state(struct otg_fsm *fsm, enum usb_otg_state old_state)
fsm->adp_prb = 0;
break;
case OTG_STATE_A_WAIT_VRISE:
- otg_del_timer(fsm, A_WAIT_VRISE);
+ otg_del_timer(otg, A_WAIT_VRISE);
fsm->a_wait_vrise_tmout = 0;
break;
case OTG_STATE_A_WAIT_BCON:
- otg_del_timer(fsm, A_WAIT_BCON);
+ otg_del_timer(otg, A_WAIT_BCON);
fsm->a_wait_bcon_tmout = 0;
break;
case OTG_STATE_A_HOST:
- otg_del_timer(fsm, A_WAIT_ENUM);
+ otg_del_timer(otg, A_WAIT_ENUM);
break;
case OTG_STATE_A_SUSPEND:
- otg_del_timer(fsm, A_AIDL_BDIS);
+ otg_del_timer(otg, A_AIDL_BDIS);
fsm->a_aidl_bdis_tmout = 0;
fsm->a_suspend_req_inf = 0;
break;
case OTG_STATE_A_PERIPHERAL:
- otg_del_timer(fsm, A_BIDL_ADIS);
+ otg_del_timer(otg, A_BIDL_ADIS);
fsm->a_bidl_adis_tmout = 0;
- if (fsm->otg->gadget)
- fsm->otg->gadget->host_request_flag = 0;
+ if (otg->gadget)
+ otg->gadget->host_request_flag = 0;
break;
case OTG_STATE_A_WAIT_VFALL:
- otg_del_timer(fsm, A_WAIT_VFALL);
+ otg_del_timer(otg, A_WAIT_VFALL);
fsm->a_wait_vfall_tmout = 0;
- otg_del_timer(fsm, A_WAIT_VRISE);
+ otg_del_timer(otg, A_WAIT_VRISE);
break;
case OTG_STATE_A_VBUS_ERR:
break;
@@ -127,17 +130,18 @@ static void otg_hnp_polling_work(struct work_struct *work)
{
struct otg_fsm *fsm = container_of(to_delayed_work(work),
struct otg_fsm, hnp_polling_work);
+ struct usb_otg *otg = container_of(fsm, struct usb_otg, fsm);
struct usb_device *udev;
- enum usb_otg_state state = fsm->otg->state;
+ enum usb_otg_state state = otg->state;
u8 flag;
int retval;

if (state != OTG_STATE_A_HOST && state != OTG_STATE_B_HOST)
return;

- udev = usb_hub_find_child(fsm->otg->host->root_hub, 1);
+ udev = usb_hub_find_child(otg->host->root_hub, 1);
if (!udev) {
- dev_err(fsm->otg->host->controller,
+ dev_err(otg->host->controller,
"no usb dev connected, can't start HNP polling\n");
return;
}
@@ -172,7 +176,7 @@ static void otg_hnp_polling_work(struct work_struct *work)
/* Host request flag is set */
if (state == OTG_STATE_A_HOST) {
/* Set b_hnp_enable */
- if (!fsm->otg->host->b_hnp_enable) {
+ if (!otg->host->b_hnp_enable) {
retval = usb_control_msg(udev,
usb_sndctrlpipe(udev, 0),
USB_REQ_SET_FEATURE, 0,
@@ -180,14 +184,14 @@ static void otg_hnp_polling_work(struct work_struct *work)
0, NULL, 0,
USB_CTRL_SET_TIMEOUT);
if (retval >= 0)
- fsm->otg->host->b_hnp_enable = 1;
+ otg->host->b_hnp_enable = 1;
}
fsm->a_bus_req = 0;
} else if (state == OTG_STATE_B_HOST) {
fsm->b_bus_req = 0;
}

- otg_statemachine(fsm);
+ otg_statemachine(otg);
}

static void otg_start_hnp_polling(struct otg_fsm *fsm)
@@ -207,133 +211,135 @@ static void otg_start_hnp_polling(struct otg_fsm *fsm)
/* Called when entering a state */
static int otg_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state)
{
- if (fsm->otg->state == new_state)
+ struct usb_otg *otg = container_of(fsm, struct usb_otg, fsm);
+
+ if (otg->state == new_state)
return 0;
dev_vdbg(fsm->dev, "Set state: %s\n", usb_otg_state_string(new_state));
- otg_leave_state(fsm, fsm->otg->state);
+ otg_leave_state(fsm, otg->state);
switch (new_state) {
case OTG_STATE_B_IDLE:
- otg_drv_vbus(fsm, 0);
- otg_chrg_vbus(fsm, 0);
- otg_loc_conn(fsm, 0);
- otg_loc_sof(fsm, 0);
+ otg_drv_vbus(otg, 0);
+ otg_chrg_vbus(otg, 0);
+ otg_loc_conn(otg, 0);
+ otg_loc_sof(otg, 0);
/*
* Driver is responsible for starting ADP probing
* if ADP sensing times out.
*/
- otg_start_adp_sns(fsm);
+ otg_start_adp_sns(otg);
otg_set_protocol(fsm, PROTO_UNDEF);
- otg_add_timer(fsm, B_SE0_SRP);
+ otg_add_timer(otg, B_SE0_SRP);
break;
case OTG_STATE_B_SRP_INIT:
- otg_start_pulse(fsm);
- otg_loc_sof(fsm, 0);
+ otg_start_pulse(otg);
+ otg_loc_sof(otg, 0);
otg_set_protocol(fsm, PROTO_UNDEF);
- otg_add_timer(fsm, B_SRP_FAIL);
+ otg_add_timer(otg, B_SRP_FAIL);
break;
case OTG_STATE_B_PERIPHERAL:
- otg_chrg_vbus(fsm, 0);
- otg_loc_sof(fsm, 0);
+ otg_chrg_vbus(otg, 0);
+ otg_loc_sof(otg, 0);
otg_set_protocol(fsm, PROTO_GADGET);
- otg_loc_conn(fsm, 1);
+ otg_loc_conn(otg, 1);
break;
case OTG_STATE_B_WAIT_ACON:
- otg_chrg_vbus(fsm, 0);
- otg_loc_conn(fsm, 0);
- otg_loc_sof(fsm, 0);
+ otg_chrg_vbus(otg, 0);
+ otg_loc_conn(otg, 0);
+ otg_loc_sof(otg, 0);
otg_set_protocol(fsm, PROTO_HOST);
- otg_add_timer(fsm, B_ASE0_BRST);
+ otg_add_timer(otg, B_ASE0_BRST);
fsm->a_bus_suspend = 0;
break;
case OTG_STATE_B_HOST:
- otg_chrg_vbus(fsm, 0);
- otg_loc_conn(fsm, 0);
- otg_loc_sof(fsm, 1);
+ otg_chrg_vbus(otg, 0);
+ otg_loc_conn(otg, 0);
+ otg_loc_sof(otg, 1);
otg_set_protocol(fsm, PROTO_HOST);
- usb_bus_start_enum(fsm->otg->host,
- fsm->otg->host->otg_port);
+ usb_bus_start_enum(otg->host, otg->host->otg_port);
otg_start_hnp_polling(fsm);
break;
case OTG_STATE_A_IDLE:
- otg_drv_vbus(fsm, 0);
- otg_chrg_vbus(fsm, 0);
- otg_loc_conn(fsm, 0);
- otg_loc_sof(fsm, 0);
- otg_start_adp_prb(fsm);
+ otg_drv_vbus(otg, 0);
+ otg_chrg_vbus(otg, 0);
+ otg_loc_conn(otg, 0);
+ otg_loc_sof(otg, 0);
+ otg_start_adp_prb(otg);
otg_set_protocol(fsm, PROTO_HOST);
break;
case OTG_STATE_A_WAIT_VRISE:
- otg_drv_vbus(fsm, 1);
- otg_loc_conn(fsm, 0);
- otg_loc_sof(fsm, 0);
+ otg_drv_vbus(otg, 1);
+ otg_loc_conn(otg, 0);
+ otg_loc_sof(otg, 0);
otg_set_protocol(fsm, PROTO_HOST);
- otg_add_timer(fsm, A_WAIT_VRISE);
+ otg_add_timer(otg, A_WAIT_VRISE);
break;
case OTG_STATE_A_WAIT_BCON:
- otg_drv_vbus(fsm, 1);
- otg_loc_conn(fsm, 0);
- otg_loc_sof(fsm, 0);
+ otg_drv_vbus(otg, 1);
+ otg_loc_conn(otg, 0);
+ otg_loc_sof(otg, 0);
otg_set_protocol(fsm, PROTO_HOST);
- otg_add_timer(fsm, A_WAIT_BCON);
+ otg_add_timer(otg, A_WAIT_BCON);
break;
case OTG_STATE_A_HOST:
- otg_drv_vbus(fsm, 1);
- otg_loc_conn(fsm, 0);
- otg_loc_sof(fsm, 1);
+ otg_drv_vbus(otg, 1);
+ otg_loc_conn(otg, 0);
+ otg_loc_sof(otg, 1);
otg_set_protocol(fsm, PROTO_HOST);
/*
* When HNP is triggered while a_bus_req = 0, a_host will
* suspend too fast to complete a_set_b_hnp_en
*/
if (!fsm->a_bus_req || fsm->a_suspend_req_inf)
- otg_add_timer(fsm, A_WAIT_ENUM);
+ otg_add_timer(otg, A_WAIT_ENUM);
otg_start_hnp_polling(fsm);
break;
case OTG_STATE_A_SUSPEND:
- otg_drv_vbus(fsm, 1);
- otg_loc_conn(fsm, 0);
- otg_loc_sof(fsm, 0);
+ otg_drv_vbus(otg, 1);
+ otg_loc_conn(otg, 0);
+ otg_loc_sof(otg, 0);
otg_set_protocol(fsm, PROTO_HOST);
- otg_add_timer(fsm, A_AIDL_BDIS);
+ otg_add_timer(otg, A_AIDL_BDIS);

break;
case OTG_STATE_A_PERIPHERAL:
- otg_loc_sof(fsm, 0);
+ otg_loc_sof(otg, 0);
otg_set_protocol(fsm, PROTO_GADGET);
- otg_drv_vbus(fsm, 1);
- otg_loc_conn(fsm, 1);
- otg_add_timer(fsm, A_BIDL_ADIS);
+ otg_drv_vbus(otg, 1);
+ otg_loc_conn(otg, 1);
+ otg_add_timer(otg, A_BIDL_ADIS);
break;
case OTG_STATE_A_WAIT_VFALL:
- otg_drv_vbus(fsm, 0);
- otg_loc_conn(fsm, 0);
- otg_loc_sof(fsm, 0);
+ otg_drv_vbus(otg, 0);
+ otg_loc_conn(otg, 0);
+ otg_loc_sof(otg, 0);
otg_set_protocol(fsm, PROTO_HOST);
- otg_add_timer(fsm, A_WAIT_VFALL);
+ otg_add_timer(otg, A_WAIT_VFALL);
break;
case OTG_STATE_A_VBUS_ERR:
- otg_drv_vbus(fsm, 0);
- otg_loc_conn(fsm, 0);
- otg_loc_sof(fsm, 0);
+ otg_drv_vbus(otg, 0);
+ otg_loc_conn(otg, 0);
+ otg_loc_sof(otg, 0);
otg_set_protocol(fsm, PROTO_UNDEF);
break;
default:
break;
}

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

/* State change judgement */
-int otg_statemachine(struct otg_fsm *fsm)
+int otg_statemachine(struct usb_otg *otg)
{
enum usb_otg_state state;
+ struct otg_fsm *fsm = &otg->fsm;

mutex_lock(&fsm->lock);

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

@@ -348,7 +354,7 @@ int otg_statemachine(struct otg_fsm *fsm)
case OTG_STATE_B_IDLE:
if (!fsm->id)
otg_set_state(fsm, OTG_STATE_A_IDLE);
- else if (fsm->b_sess_vld && fsm->otg->gadget)
+ else if (fsm->b_sess_vld && otg->gadget)
otg_set_state(fsm, OTG_STATE_B_PERIPHERAL);
else if ((fsm->b_bus_req || fsm->adp_change || fsm->power_up) &&
fsm->b_ssend_srp && fsm->b_se0_srp)
@@ -361,8 +367,8 @@ int otg_statemachine(struct otg_fsm *fsm)
case OTG_STATE_B_PERIPHERAL:
if (!fsm->id || !fsm->b_sess_vld)
otg_set_state(fsm, OTG_STATE_B_IDLE);
- else if (fsm->b_bus_req && fsm->otg->
- gadget->b_hnp_enable && fsm->a_bus_suspend)
+ else if (fsm->b_bus_req && otg->gadget->b_hnp_enable
+ && fsm->a_bus_suspend)
otg_set_state(fsm, OTG_STATE_B_WAIT_ACON);
break;
case OTG_STATE_B_WAIT_ACON:
@@ -407,7 +413,7 @@ int otg_statemachine(struct otg_fsm *fsm)
if (fsm->id || fsm->a_bus_drop)
otg_set_state(fsm, OTG_STATE_A_WAIT_VFALL);
else if ((!fsm->a_bus_req || fsm->a_suspend_req_inf) &&
- fsm->otg->host->b_hnp_enable)
+ otg->host->b_hnp_enable)
otg_set_state(fsm, OTG_STATE_A_SUSPEND);
else if (!fsm->b_conn)
otg_set_state(fsm, OTG_STATE_A_WAIT_BCON);
@@ -415,9 +421,9 @@ int otg_statemachine(struct otg_fsm *fsm)
otg_set_state(fsm, OTG_STATE_A_VBUS_ERR);
break;
case OTG_STATE_A_SUSPEND:
- if (!fsm->b_conn && fsm->otg->host->b_hnp_enable)
+ if (!fsm->b_conn && otg->host->b_hnp_enable)
otg_set_state(fsm, OTG_STATE_A_PERIPHERAL);
- else if (!fsm->b_conn && !fsm->otg->host->b_hnp_enable)
+ else if (!fsm->b_conn && !otg->host->b_hnp_enable)
otg_set_state(fsm, OTG_STATE_A_WAIT_BCON);
else if (fsm->a_bus_req || fsm->b_bus_resume)
otg_set_state(fsm, OTG_STATE_A_HOST);
diff --git a/drivers/usb/phy/phy-fsl-usb.c b/drivers/usb/phy/phy-fsl-usb.c
index c57ef5c..a18a2ee 100644
--- a/drivers/usb/phy/phy-fsl-usb.c
+++ b/drivers/usb/phy/phy-fsl-usb.c
@@ -127,7 +127,7 @@ int write_ulpi(u8 addr, u8 data)
/* Operations that will be called from OTG Finite State Machine */

/* Charge vbus for vbus pulsing in SRP */
-void fsl_otg_chrg_vbus(struct otg_fsm *fsm, int on)
+void fsl_otg_chrg_vbus(struct usb_otg *otg, int on)
{
u32 tmp;

@@ -163,7 +163,7 @@ void fsl_otg_dischrg_vbus(int on)
}

/* A-device driver vbus, controlled through PP bit in PORTSC */
-void fsl_otg_drv_vbus(struct otg_fsm *fsm, int on)
+void fsl_otg_drv_vbus(struct usb_otg *otg, int on)
{
u32 tmp;

@@ -181,7 +181,7 @@ void fsl_otg_drv_vbus(struct otg_fsm *fsm, int on)
* Pull-up D+, signalling connect by periperal. Also used in
* data-line pulsing in SRP
*/
-void fsl_otg_loc_conn(struct otg_fsm *fsm, int on)
+void fsl_otg_loc_conn(struct usb_otg *otg, int on)
{
u32 tmp;

@@ -200,7 +200,7 @@ void fsl_otg_loc_conn(struct otg_fsm *fsm, int on)
* port. In host mode, controller will automatically send SOF.
* Suspend will block the data on the port.
*/
-void fsl_otg_loc_sof(struct otg_fsm *fsm, int on)
+void fsl_otg_loc_sof(struct usb_otg *otg, int on)
{
u32 tmp;

@@ -215,7 +215,7 @@ void fsl_otg_loc_sof(struct otg_fsm *fsm, int on)
}

/* Start SRP pulsing by data-line pulsing, followed with v-bus pulsing. */
-void fsl_otg_start_pulse(struct otg_fsm *fsm)
+void fsl_otg_start_pulse(struct usb_otg *otg)
{
u32 tmp;

@@ -228,7 +228,7 @@ void fsl_otg_start_pulse(struct otg_fsm *fsm)
fsl_otg_loc_conn(1);
#endif

- fsl_otg_add_timer(fsm, b_data_pulse_tmr);
+ fsl_otg_add_timer(&otg->fsm, b_data_pulse_tmr);
}

void b_data_pulse_end(unsigned long foo)
@@ -245,14 +245,14 @@ void b_data_pulse_end(unsigned long foo)
void fsl_otg_pulse_vbus(void)
{
srp_wait_done = 0;
- fsl_otg_chrg_vbus(&fsl_otg_dev->fsm, 1);
+ fsl_otg_chrg_vbus(&fsl_otg_dev->otg, 1);
/* start the timer to end vbus charge */
- fsl_otg_add_timer(&fsl_otg_dev->fsm, b_vbus_pulse_tmr);
+ fsl_otg_add_timer(&fsl_otg_dev->otg.fsm, b_vbus_pulse_tmr);
}

void b_vbus_pulse_end(unsigned long foo)
{
- fsl_otg_chrg_vbus(&fsl_otg_dev->fsm, 0);
+ fsl_otg_chrg_vbus(&fsl_otg_dev->otg, 0);

/*
* As USB3300 using the same a_sess_vld and b_sess_vld voltage
@@ -260,7 +260,7 @@ void b_vbus_pulse_end(unsigned long foo)
* residual voltage of vbus pulsing and A device pull up
*/
fsl_otg_dischrg_vbus(1);
- fsl_otg_add_timer(&fsl_otg_dev->fsm, b_srp_wait_tmr);
+ fsl_otg_add_timer(&fsl_otg_dev->otg.fsm, b_srp_wait_tmr);
}

void b_srp_end(unsigned long foo)
@@ -269,8 +269,8 @@ void b_srp_end(unsigned long foo)
srp_wait_done = 1;

if ((fsl_otg_dev->phy.otg->state == OTG_STATE_B_SRP_INIT) &&
- fsl_otg_dev->fsm.b_sess_vld)
- fsl_otg_dev->fsm.b_srp_done = 1;
+ fsl_otg_dev->otg.fsm.b_sess_vld)
+ fsl_otg_dev->otg.fsm.b_srp_done = 1;
}

/*
@@ -282,9 +282,9 @@ void a_wait_enum(unsigned long foo)
{
VDBG("a_wait_enum timeout\n");
if (!fsl_otg_dev->phy.otg->host->b_hnp_enable)
- fsl_otg_add_timer(&fsl_otg_dev->fsm, a_wait_enum_tmr);
+ fsl_otg_add_timer(&fsl_otg_dev->otg.fsm, a_wait_enum_tmr);
else
- otg_statemachine(&fsl_otg_dev->fsm);
+ otg_statemachine(&fsl_otg_dev->otg);
}

/* The timeout callback function to set time out bit */
@@ -421,7 +421,7 @@ void fsl_otg_add_timer(struct otg_fsm *fsm, void *gtimer)
list_add_tail(&timer->list, &active_timers);
}

-static void fsl_otg_fsm_add_timer(struct otg_fsm *fsm, enum otg_fsm_timer t)
+static void fsl_otg_fsm_add_timer(struct usb_otg *otg, enum otg_fsm_timer t)
{
struct fsl_otg_timer *timer;

@@ -429,7 +429,7 @@ static void fsl_otg_fsm_add_timer(struct otg_fsm *fsm, enum otg_fsm_timer t)
if (!timer)
return;

- fsl_otg_add_timer(fsm, timer);
+ fsl_otg_add_timer(&otg->fsm, timer);
}

/* Remove timer from the timer list; clear timeout status */
@@ -443,7 +443,7 @@ void fsl_otg_del_timer(struct otg_fsm *fsm, void *gtimer)
list_del(&timer->list);
}

-static void fsl_otg_fsm_del_timer(struct otg_fsm *fsm, enum otg_fsm_timer t)
+static void fsl_otg_fsm_del_timer(struct usb_otg *otg, enum otg_fsm_timer t)
{
struct fsl_otg_timer *timer;

@@ -451,7 +451,7 @@ static void fsl_otg_fsm_del_timer(struct otg_fsm *fsm, enum otg_fsm_timer t)
if (!timer)
return;

- fsl_otg_del_timer(fsm, timer);
+ fsl_otg_del_timer(&otg->fsm, timer);
}

/* Reset controller, not reset the bus */
@@ -467,9 +467,9 @@ void otg_reset_controller(void)
}

/* Call suspend/resume routines in host driver */
-int fsl_otg_start_host(struct otg_fsm *fsm, int on)
+int fsl_otg_start_host(struct usb_otg *otg, int on)
{
- struct usb_otg *otg = fsm->otg;
+ struct otg_fsm *fsm = &otg->fsm;
struct device *dev;
struct fsl_otg *otg_dev =
container_of(otg->usb_phy, struct fsl_otg, phy);
@@ -496,7 +496,7 @@ int fsl_otg_start_host(struct otg_fsm *fsm, int on)
retval = dev->driver->pm->resume(dev);
if (fsm->id) {
/* default-b */
- fsl_otg_drv_vbus(fsm, 1);
+ fsl_otg_drv_vbus(otg, 1);
/*
* Workaround: b_host can't driver
* vbus, but PP in PORTSC needs to
@@ -521,7 +521,7 @@ int fsl_otg_start_host(struct otg_fsm *fsm, int on)
retval = dev->driver->pm->suspend(dev);
if (fsm->id)
/* default-b */
- fsl_otg_drv_vbus(fsm, 0);
+ fsl_otg_drv_vbus(otg, 0);
}
otg_dev->host_working = 0;
}
@@ -534,9 +534,8 @@ end:
* Call suspend and resume function in udc driver
* to stop and start udc driver.
*/
-int fsl_otg_start_gadget(struct otg_fsm *fsm, int on)
+int fsl_otg_start_gadget(struct usb_otg *otg, int on)
{
- struct usb_otg *otg = fsm->otg;
struct device *dev;

if (!otg->gadget || !otg->gadget->dev.parent)
@@ -573,14 +572,14 @@ static int fsl_otg_set_host(struct usb_otg *otg, struct usb_bus *host)

otg->host = host;

- otg_dev->fsm.a_bus_drop = 0;
- otg_dev->fsm.a_bus_req = 1;
+ otg->fsm.a_bus_drop = 0;
+ otg->fsm.a_bus_req = 1;

if (host) {
VDBG("host off......\n");

otg->host->otg_port = fsl_otg_initdata.otg_port;
- otg->host->is_b_host = otg_dev->fsm.id;
+ otg->host->is_b_host = otg->fsm.id;
/*
* must leave time for hub_wq to finish its thing
* before yanking the host driver out from under it,
@@ -594,7 +593,7 @@ static int fsl_otg_set_host(struct usb_otg *otg, struct usb_bus *host)
if (!(fsl_readl(&otg_dev->dr_mem_map->otgsc) &
OTGSC_STS_USB_ID)) {
/* Mini-A cable connected */
- struct otg_fsm *fsm = &otg_dev->fsm;
+ struct otg_fsm *fsm = &otg->fsm;

otg->state = OTG_STATE_UNDEFINED;
fsm->protocol = PROTO_UNDEF;
@@ -603,7 +602,7 @@ static int fsl_otg_set_host(struct usb_otg *otg, struct usb_bus *host)

otg_dev->host_working = 0;

- otg_statemachine(&otg_dev->fsm);
+ otg_statemachine(otg);

return 0;
}
@@ -628,22 +627,22 @@ static int fsl_otg_set_peripheral(struct usb_otg *otg,
otg->gadget->ops->vbus_draw(otg->gadget, 0);
usb_gadget_vbus_disconnect(otg->gadget);
otg->gadget = 0;
- otg_dev->fsm.b_bus_req = 0;
- otg_statemachine(&otg_dev->fsm);
+ otg->fsm.b_bus_req = 0;
+ otg_statemachine(otg);
return 0;
}

otg->gadget = gadget;
- otg->gadget->is_a_peripheral = !otg_dev->fsm.id;
+ otg->gadget->is_a_peripheral = !otg->fsm.id;

- otg_dev->fsm.b_bus_req = 1;
+ otg->fsm.b_bus_req = 1;

/* start the gadget right away if the ID pin says Mini-B */
- pr_debug("ID pin=%d\n", otg_dev->fsm.id);
- if (otg_dev->fsm.id == 1) {
- fsl_otg_start_host(&otg_dev->fsm, 0);
- otg_drv_vbus(&otg_dev->fsm, 0);
- fsl_otg_start_gadget(&otg_dev->fsm, 1);
+ pr_debug("ID pin=%d\n", otg->fsm.id);
+ if (otg->fsm.id == 1) {
+ fsl_otg_start_host(otg, 0);
+ otg_drv_vbus(otg, 0);
+ fsl_otg_start_gadget(otg, 1);
}

return 0;
@@ -672,13 +671,14 @@ static int fsl_otg_set_power(struct usb_phy *phy, unsigned mA)
*/
static void fsl_otg_event(struct work_struct *work)
{
- struct fsl_otg *og = container_of(work, struct fsl_otg, otg_event.work);
- struct otg_fsm *fsm = &og->fsm;
-
- if (fsm->id) { /* switch to gadget */
- fsl_otg_start_host(fsm, 0);
- otg_drv_vbus(fsm, 0);
- fsl_otg_start_gadget(fsm, 1);
+ struct fsl_otg *fotg = container_of(work, struct fsl_otg,
+ otg_event.work);
+ struct usb_otg *otg = &fotg->otg;
+
+ if (otg->fsm.id) { /* switch to gadget */
+ fsl_otg_start_host(otg, 0);
+ otg_drv_vbus(otg, 0);
+ fsl_otg_start_gadget(otg, 1);
}
}

@@ -694,8 +694,8 @@ static int fsl_otg_start_srp(struct usb_otg *otg)
if (otg_dev != fsl_otg_dev)
return -ENODEV;

- otg_dev->fsm.b_bus_req = 1;
- otg_statemachine(&otg_dev->fsm);
+ otg->fsm.b_bus_req = 1;
+ otg_statemachine(otg);

return 0;
}
@@ -715,8 +715,8 @@ static int fsl_otg_start_hnp(struct usb_otg *otg)
pr_debug("start_hnp...\n");

/* clear a_bus_req to enter a_suspend state */
- otg_dev->fsm.a_bus_req = 0;
- otg_statemachine(&otg_dev->fsm);
+ otg->fsm.a_bus_req = 0;
+ otg_statemachine(otg);

return 0;
}
@@ -729,8 +729,8 @@ static int fsl_otg_start_hnp(struct usb_otg *otg)
*/
irqreturn_t fsl_otg_isr(int irq, void *dev_id)
{
- struct otg_fsm *fsm = &((struct fsl_otg *)dev_id)->fsm;
- struct usb_otg *otg = ((struct fsl_otg *)dev_id)->phy.otg;
+ struct usb_otg *otg = &((struct fsl_otg *)dev_id)->otg;
+ struct otg_fsm *fsm = &otg->fsm;
u32 otg_int_src, otg_sc;

otg_sc = fsl_readl(&usb_dr_regs->otgsc);
@@ -768,9 +768,9 @@ irqreturn_t fsl_otg_isr(int irq, void *dev_id)
cancel_delayed_work(&
((struct fsl_otg *)dev_id)->
otg_event);
- fsl_otg_start_gadget(fsm, 0);
- otg_drv_vbus(fsm, 1);
- fsl_otg_start_host(fsm, 1);
+ fsl_otg_start_gadget(otg, 0);
+ otg_drv_vbus(otg, 1);
+ fsl_otg_start_host(otg, 1);
}
return IRQ_HANDLED;
}
@@ -806,25 +806,21 @@ static int fsl_otg_conf(struct platform_device *pdev)
if (!fsl_otg_tc)
return -ENOMEM;

- fsl_otg_tc->phy.otg = kzalloc(sizeof(struct usb_otg), GFP_KERNEL);
- if (!fsl_otg_tc->phy.otg) {
- kfree(fsl_otg_tc);
- return -ENOMEM;
- }
+ fsl_otg_tc->phy.otg = &fsl_otg_tc->otg;

INIT_DELAYED_WORK(&fsl_otg_tc->otg_event, fsl_otg_event);

INIT_LIST_HEAD(&active_timers);
- status = fsl_otg_init_timers(&fsl_otg_tc->fsm);
+ status = fsl_otg_init_timers(&fsl_otg_tc->otg.fsm);
if (status) {
pr_info("Couldn't init OTG timers\n");
goto err;
}
- mutex_init(&fsl_otg_tc->fsm.lock);
+ mutex_init(&fsl_otg_tc->otg.fsm.lock);

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

/* initialize the otg structure */
fsl_otg_tc->phy.label = DRIVER_DESC;
@@ -859,18 +855,15 @@ int usb_otg_start(struct platform_device *pdev)
{
struct fsl_otg *p_otg;
struct usb_phy *otg_trans = usb_get_phy(USB_PHY_TYPE_USB2);
- struct otg_fsm *fsm;
int status;
struct resource *res;
u32 temp;
struct fsl_usb2_platform_data *pdata = dev_get_platdata(&pdev->dev);

p_otg = container_of(otg_trans, struct fsl_otg, phy);
- fsm = &p_otg->fsm;

/* Initialize the state machine structure with default values */
SET_OTG_STATE(otg_trans, OTG_STATE_UNDEFINED);
- fsm->otg = p_otg->phy.otg;

/* We don't require predefined MEM/IRQ resource index */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -964,13 +957,13 @@ int usb_otg_start(struct platform_device *pdev)
*/
if (fsl_readl(&p_otg->dr_mem_map->otgsc) & OTGSC_STS_USB_ID) {
p_otg->phy.otg->state = OTG_STATE_UNDEFINED;
- p_otg->fsm.id = 1;
+ p_otg->otg.fsm.id = 1;
} else {
p_otg->phy.otg->state = OTG_STATE_A_IDLE;
- p_otg->fsm.id = 0;
+ p_otg->otg.fsm.id = 0;
}

- pr_debug("initial ID pin=%d\n", p_otg->fsm.id);
+ pr_debug("initial ID pin=%d\n", p_otg->otg.fsm.id);

/* enable OTG ID pin interrupt */
temp = fsl_readl(&p_otg->dr_mem_map->otgsc);
@@ -987,7 +980,7 @@ int usb_otg_start(struct platform_device *pdev)
static int show_fsl_usb2_otg_state(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct otg_fsm *fsm = &fsl_otg_dev->fsm;
+ struct otg_fsm *fsm = &fsl_otg_dev->otg.fsm;
char *next = buf;
unsigned size = PAGE_SIZE;
int t;
@@ -1085,26 +1078,26 @@ static long fsl_otg_ioctl(struct file *file, unsigned int cmd,
break;

case SET_A_SUSPEND_REQ:
- fsl_otg_dev->fsm.a_suspend_req_inf = arg;
+ fsl_otg_dev->otg.fsm.a_suspend_req_inf = arg;
break;

case SET_A_BUS_DROP:
- fsl_otg_dev->fsm.a_bus_drop = arg;
+ fsl_otg_dev->otg.fsm.a_bus_drop = arg;
break;

case SET_A_BUS_REQ:
- fsl_otg_dev->fsm.a_bus_req = arg;
+ fsl_otg_dev->otg.fsm.a_bus_req = arg;
break;

case SET_B_BUS_REQ:
- fsl_otg_dev->fsm.b_bus_req = arg;
+ fsl_otg_dev->otg.fsm.b_bus_req = arg;
break;

default:
break;
}

- otg_statemachine(&fsl_otg_dev->fsm);
+ otg_statemachine(&fsl_otg_dev->otg);

return retval;
}
diff --git a/drivers/usb/phy/phy-fsl-usb.h b/drivers/usb/phy/phy-fsl-usb.h
index 2314995..e207cfb 100644
--- a/drivers/usb/phy/phy-fsl-usb.h
+++ b/drivers/usb/phy/phy-fsl-usb.h
@@ -15,7 +15,6 @@
* 675 Mass Ave, Cambridge, MA 02139, USA.
*/

-#include <linux/usb/otg-fsm.h>
#include <linux/usb/otg.h>
#include <linux/ioctl.h>

@@ -370,7 +369,7 @@ inline struct fsl_otg_timer *otg_timer_initializer

struct fsl_otg {
struct usb_phy phy;
- struct otg_fsm fsm;
+ struct usb_otg otg;
struct usb_dr_mmap *dr_mem_map;
struct delayed_work otg_event;

diff --git a/include/linux/usb/otg-fsm.h b/include/linux/usb/otg-fsm.h
index 47b8392..36f0cf9 100644
--- a/include/linux/usb/otg-fsm.h
+++ b/include/linux/usb/otg-fsm.h
@@ -189,7 +189,6 @@ struct otg_fsm {
int a_bidl_adis_tmout;

struct otg_fsm_ops *ops;
- struct usb_otg *otg;

/* Current usb protocol used: 0:undefine; 1:host; 2:client */
int protocol;
@@ -202,126 +201,23 @@ struct otg_fsm {
struct device *dev;
};

+struct usb_otg;
+
struct otg_fsm_ops {
- void (*chrg_vbus)(struct otg_fsm *fsm, int on);
- void (*drv_vbus)(struct otg_fsm *fsm, int on);
- void (*loc_conn)(struct otg_fsm *fsm, int on);
- void (*loc_sof)(struct otg_fsm *fsm, int on);
- void (*start_pulse)(struct otg_fsm *fsm);
- void (*start_adp_prb)(struct otg_fsm *fsm);
- void (*start_adp_sns)(struct otg_fsm *fsm);
- void (*add_timer)(struct otg_fsm *fsm, enum otg_fsm_timer timer);
- void (*del_timer)(struct otg_fsm *fsm, enum otg_fsm_timer timer);
- int (*start_host)(struct otg_fsm *fsm, int on);
- int (*start_gadget)(struct otg_fsm *fsm, int on);
+ void (*chrg_vbus)(struct usb_otg *otg, int on);
+ void (*drv_vbus)(struct usb_otg *otg, int on);
+ void (*loc_conn)(struct usb_otg *otg, int on);
+ void (*loc_sof)(struct usb_otg *otg, int on);
+ void (*start_pulse)(struct usb_otg *otg);
+ void (*start_adp_prb)(struct usb_otg *otg);
+ void (*start_adp_sns)(struct usb_otg *otg);
+ void (*add_timer)(struct usb_otg *otg, enum otg_fsm_timer timer);
+ void (*del_timer)(struct usb_otg *otg, enum otg_fsm_timer timer);
+ int (*start_host)(struct usb_otg *otg, int on);
+ int (*start_gadget)(struct usb_otg *otg, int on);
};


-static inline int otg_chrg_vbus(struct otg_fsm *fsm, int on)
-{
- if (!fsm->ops->chrg_vbus)
- return -EOPNOTSUPP;
- fsm->ops->chrg_vbus(fsm, on);
- return 0;
-}
-
-static inline int otg_drv_vbus(struct otg_fsm *fsm, int on)
-{
- if (!fsm->ops->drv_vbus)
- return -EOPNOTSUPP;
- if (fsm->drv_vbus != on) {
- fsm->drv_vbus = on;
- fsm->ops->drv_vbus(fsm, on);
- }
- return 0;
-}
-
-static inline int otg_loc_conn(struct otg_fsm *fsm, int on)
-{
- if (!fsm->ops->loc_conn)
- return -EOPNOTSUPP;
- if (fsm->loc_conn != on) {
- fsm->loc_conn = on;
- fsm->ops->loc_conn(fsm, on);
- }
- return 0;
-}
-
-static inline int otg_loc_sof(struct otg_fsm *fsm, int on)
-{
- if (!fsm->ops->loc_sof)
- return -EOPNOTSUPP;
- if (fsm->loc_sof != on) {
- fsm->loc_sof = on;
- fsm->ops->loc_sof(fsm, on);
- }
- return 0;
-}
-
-static inline int otg_start_pulse(struct otg_fsm *fsm)
-{
- if (!fsm->ops->start_pulse)
- return -EOPNOTSUPP;
- if (!fsm->data_pulse) {
- fsm->data_pulse = 1;
- fsm->ops->start_pulse(fsm);
- }
- return 0;
-}
-
-static inline int otg_start_adp_prb(struct otg_fsm *fsm)
-{
- if (!fsm->ops->start_adp_prb)
- return -EOPNOTSUPP;
- if (!fsm->adp_prb) {
- fsm->adp_sns = 0;
- fsm->adp_prb = 1;
- fsm->ops->start_adp_prb(fsm);
- }
- return 0;
-}
-
-static inline int otg_start_adp_sns(struct otg_fsm *fsm)
-{
- if (!fsm->ops->start_adp_sns)
- return -EOPNOTSUPP;
- if (!fsm->adp_sns) {
- fsm->adp_sns = 1;
- fsm->ops->start_adp_sns(fsm);
- }
- return 0;
-}
-
-static inline int otg_add_timer(struct otg_fsm *fsm, enum otg_fsm_timer timer)
-{
- if (!fsm->ops->add_timer)
- return -EOPNOTSUPP;
- fsm->ops->add_timer(fsm, timer);
- return 0;
-}
-
-static inline int otg_del_timer(struct otg_fsm *fsm, enum otg_fsm_timer timer)
-{
- if (!fsm->ops->del_timer)
- return -EOPNOTSUPP;
- fsm->ops->del_timer(fsm, timer);
- return 0;
-}
-
-static inline int otg_start_host(struct otg_fsm *fsm, int on)
-{
- if (!fsm->ops->start_host)
- return -EOPNOTSUPP;
- return fsm->ops->start_host(fsm, on);
-}
-
-static inline int otg_start_gadget(struct otg_fsm *fsm, int on)
-{
- if (!fsm->ops->start_gadget)
- return -EOPNOTSUPP;
- return fsm->ops->start_gadget(fsm, on);
-}
-
-int otg_statemachine(struct otg_fsm *fsm);
+int otg_statemachine(struct usb_otg *otg);

#endif /* __LINUX_USB_OTG_FSM_H */
diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h
index 67929df..e8a14dc 100644
--- a/include/linux/usb/otg.h
+++ b/include/linux/usb/otg.h
@@ -11,6 +11,7 @@

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

struct usb_otg {
u8 default_a;
@@ -22,6 +23,7 @@ struct usb_otg {
struct usb_gadget *gadget;

enum usb_otg_state state;
+ struct otg_fsm fsm;

/* bind/unbind the host controller */
int (*set_host)(struct usb_otg *otg, struct usb_bus *host);
@@ -128,4 +130,109 @@ enum usb_dr_mode {
*/
extern enum usb_dr_mode usb_get_dr_mode(struct device *dev);

+static inline int otg_chrg_vbus(struct usb_otg *otg, int on)
+{
+ if (!otg->fsm.ops->chrg_vbus)
+ return -EOPNOTSUPP;
+ otg->fsm.ops->chrg_vbus(otg, on);
+ return 0;
+}
+
+static inline int otg_drv_vbus(struct usb_otg *otg, int on)
+{
+ if (!otg->fsm.ops->drv_vbus)
+ return -EOPNOTSUPP;
+ if (otg->fsm.drv_vbus != on) {
+ otg->fsm.drv_vbus = on;
+ otg->fsm.ops->drv_vbus(otg, on);
+ }
+ return 0;
+}
+
+static inline int otg_loc_conn(struct usb_otg *otg, int on)
+{
+ if (!otg->fsm.ops->loc_conn)
+ return -EOPNOTSUPP;
+ if (otg->fsm.loc_conn != on) {
+ otg->fsm.loc_conn = on;
+ otg->fsm.ops->loc_conn(otg, on);
+ }
+ return 0;
+}
+
+static inline int otg_loc_sof(struct usb_otg *otg, int on)
+{
+ if (!otg->fsm.ops->loc_sof)
+ return -EOPNOTSUPP;
+ if (otg->fsm.loc_sof != on) {
+ otg->fsm.loc_sof = on;
+ otg->fsm.ops->loc_sof(otg, on);
+ }
+ return 0;
+}
+
+static inline int otg_start_pulse(struct usb_otg *otg)
+{
+ if (!otg->fsm.ops->start_pulse)
+ return -EOPNOTSUPP;
+ if (!otg->fsm.data_pulse) {
+ otg->fsm.data_pulse = 1;
+ otg->fsm.ops->start_pulse(otg);
+ }
+ return 0;
+}
+
+static inline int otg_start_adp_prb(struct usb_otg *otg)
+{
+ if (!otg->fsm.ops->start_adp_prb)
+ return -EOPNOTSUPP;
+ if (!otg->fsm.adp_prb) {
+ otg->fsm.adp_sns = 0;
+ otg->fsm.adp_prb = 1;
+ otg->fsm.ops->start_adp_prb(otg);
+ }
+ return 0;
+}
+
+static inline int otg_start_adp_sns(struct usb_otg *otg)
+{
+ if (!otg->fsm.ops->start_adp_sns)
+ return -EOPNOTSUPP;
+ if (!otg->fsm.adp_sns) {
+ otg->fsm.adp_sns = 1;
+ otg->fsm.ops->start_adp_sns(otg);
+ }
+ return 0;
+}
+
+static inline int otg_add_timer(struct usb_otg *otg, enum otg_fsm_timer timer)
+{
+ if (!otg->fsm.ops->add_timer)
+ return -EOPNOTSUPP;
+ otg->fsm.ops->add_timer(otg, timer);
+ return 0;
+}
+
+static inline int otg_del_timer(struct usb_otg *otg, enum otg_fsm_timer timer)
+{
+ if (!otg->fsm.ops->del_timer)
+ return -EOPNOTSUPP;
+ otg->fsm.ops->del_timer(otg, timer);
+ return 0;
+}
+
+static inline int otg_start_host(struct usb_otg *otg, int on)
+{
+ if (!otg->fsm.ops->start_host)
+ return -EOPNOTSUPP;
+ return otg->fsm.ops->start_host(otg, on);
+}
+
+static inline int otg_start_gadget(struct usb_otg *otg, int on)
+{
+ if (!otg->fsm.ops->start_gadget)
+ return -EOPNOTSUPP;
+ return otg->fsm.ops->start_gadget(otg, on);
+}
+
#endif /* __LINUX_USB_OTG_H */
--
2.5.0

2016-04-05 14:06:27

by Roger Quadros

[permalink] [raw]
Subject: [PATCH v6 05/12] usb: gadget.h: Add OTG to gadget interface

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

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

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

diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h
index 5d4e151..8c0ae64 100644
--- a/include/linux/usb/gadget.h
+++ b/include/linux/usb/gadget.h
@@ -1100,6 +1100,20 @@ struct usb_gadget_driver {
};


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

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

--
2.5.0

2016-04-05 14:06:35

by Roger Quadros

[permalink] [raw]
Subject: [PATCH v6 12/12] usb: host: xhci-plat: Add otg device to platform data

Host controllers that are part of an OTG/dual-role instance
need to somehow pass the OTG controller device information
to the HCD core.

We use platform data to pass the OTG controller device.

Signed-off-by: Roger Quadros <[email protected]>
---
drivers/usb/host/xhci-plat.c | 35 ++++++++++++++++++++++++++++-------
include/linux/usb/xhci_pdriver.h | 3 +++
2 files changed, 31 insertions(+), 7 deletions(-)

diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c
index 5c15e9b..3d0f597 100644
--- a/drivers/usb/host/xhci-plat.c
+++ b/drivers/usb/host/xhci-plat.c
@@ -230,11 +230,20 @@ static int xhci_plat_probe(struct platform_device *pdev)
goto put_usb3_hcd;
}

- ret = usb_add_hcd(hcd, irq, IRQF_SHARED);
+ if (pdata->otg_dev)
+ ret = usb_otg_add_hcd(hcd, irq, IRQF_SHARED, pdata->otg_dev);
+ else
+ ret = usb_add_hcd(hcd, irq, IRQF_SHARED);
+
if (ret)
goto disable_usb_phy;

- ret = usb_add_hcd(xhci->shared_hcd, irq, IRQF_SHARED);
+ if (pdata->otg_dev)
+ ret = usb_otg_add_hcd(xhci->shared_hcd, irq, IRQF_SHARED,
+ pdata->otg_dev);
+ else
+ ret = usb_add_hcd(xhci->shared_hcd, irq, IRQF_SHARED);
+
if (ret)
goto dealloc_usb2_hcd;

@@ -242,7 +251,10 @@ static int xhci_plat_probe(struct platform_device *pdev)


dealloc_usb2_hcd:
- usb_remove_hcd(hcd);
+ if (pdata->otg_dev)
+ usb_otg_remove_hcd(hcd);
+ else
+ usb_remove_hcd(hcd);

disable_usb_phy:
usb_phy_shutdown(hcd->usb_phy);
@@ -260,16 +272,25 @@ put_hcd:
return ret;
}

-static int xhci_plat_remove(struct platform_device *dev)
+static int xhci_plat_remove(struct platform_device *pdev)
{
- struct usb_hcd *hcd = platform_get_drvdata(dev);
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
struct clk *clk = xhci->clk;
+ struct usb_xhci_pdata *pdata = dev_get_platdata(&pdev->dev);
+
+ if (pdata->otg_dev)
+ usb_otg_remove_hcd(xhci->shared_hcd);
+ else
+ usb_remove_hcd(xhci->shared_hcd);

- usb_remove_hcd(xhci->shared_hcd);
usb_phy_shutdown(hcd->usb_phy);

- usb_remove_hcd(hcd);
+ if (pdata->otg_dev)
+ usb_otg_remove_hcd(hcd);
+ else
+ usb_remove_hcd(hcd);
+
usb_put_hcd(xhci->shared_hcd);

if (!IS_ERR(clk))
diff --git a/include/linux/usb/xhci_pdriver.h b/include/linux/usb/xhci_pdriver.h
index 376654b..5c68b83 100644
--- a/include/linux/usb/xhci_pdriver.h
+++ b/include/linux/usb/xhci_pdriver.h
@@ -18,10 +18,13 @@
*
* @usb3_lpm_capable: determines if this xhci platform supports USB3
* LPM capability
+ * @otg_dev: OTG controller device. Only requied if part of
+ * OTG/dual-role.
*
*/
struct usb_xhci_pdata {
unsigned usb3_lpm_capable:1;
+ struct device *otg_dev;
};

#endif /* __USB_CORE_XHCI_PDRIVER_H */
--
2.5.0

2016-04-05 14:06:37

by Roger Quadros

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

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

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

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

diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 38cc4ba..27e3b4c 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -2273,6 +2273,7 @@ static int usb_enumerate_device_otg(struct usb_device *udev)
&& udev->parent == udev->bus->root_hub) {
struct usb_otg_descriptor *desc = NULL;
struct usb_bus *bus = udev->bus;
+ struct usb_hcd *hcd = bus_to_hcd(bus);
unsigned port1 = udev->portnum;

/* descriptor may appear anywhere in config */
@@ -2302,6 +2303,9 @@ static int usb_enumerate_device_otg(struct usb_device *udev)
dev_err(&udev->dev, "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(hcd->otg_dev);
}
} else if (desc->bLength == sizeof
(struct usb_otg_descriptor)) {
@@ -2312,10 +2316,14 @@ static int usb_enumerate_device_otg(struct usb_device *udev)
USB_DEVICE_A_ALT_HNP_SUPPORT,
0, NULL, 0,
USB_CTRL_SET_TIMEOUT);
- if (err < 0)
+ if (err < 0) {
dev_err(&udev->dev,
"set a_alt_hnp_support failed: %d\n",
err);
+ } else {
+ /* notify OTG fsm about a_set_b_hnp_enable */
+ usb_otg_kick_fsm(hcd->otg_dev);
+ }
}
}
#endif
@@ -4355,8 +4363,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(hcd->otg_dev);
+#endif
+ }
}

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

2016-04-05 14:08:48

by Roger Quadros

[permalink] [raw]
Subject: [PATCH v6 07/12] usb: otg: add OTG/dual-role core

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 <[email protected]>
---
drivers/usb/common/Makefile | 2 +-
drivers/usb/common/usb-otg.c | 1058 ++++++++++++++++++++++++++++++++++++++++++
drivers/usb/common/usb-otg.h | 71 +++
drivers/usb/core/Kconfig | 2 +-
include/linux/usb/gadget.h | 2 +
include/linux/usb/hcd.h | 1 +
include/linux/usb/otg-fsm.h | 7 +
include/linux/usb/otg.h | 154 +++++-
8 files changed, 1292 insertions(+), 5 deletions(-)
create mode 100644 drivers/usb/common/usb-otg.c
create mode 100644 drivers/usb/common/usb-otg.h

diff --git a/drivers/usb/common/Makefile b/drivers/usb/common/Makefile
index f8f2c88..730d928 100644
--- a/drivers/usb/common/Makefile
+++ b/drivers/usb/common/Makefile
@@ -7,5 +7,5 @@ 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
+usbotg-y := usb-otg.o usb-otg-fsm.o
obj-$(CONFIG_USB_OTG) += usbotg.o
diff --git a/drivers/usb/common/usb-otg.c b/drivers/usb/common/usb-otg.c
new file mode 100644
index 0000000..41e762a
--- /dev/null
+++ b/drivers/usb/common/usb-otg.c
@@ -0,0 +1,1058 @@
+/**
+ * drivers/usb/common/usb-otg.c - USB OTG core
+ *
+ * Copyright (C) 2016 Texas Instruments Incorporated - http://www.ti.com
+ * Author: Roger Quadros <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/ktime.h>
+#include <linux/hrtimer.h>
+#include <linux/list.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/usb/otg.h>
+#include <linux/usb/gadget.h>
+#include <linux/workqueue.h>
+
+#include "usb-otg.h"
+
+struct otg_gcd {
+ struct usb_gadget *gadget;
+ struct otg_gadget_ops *ops;
+};
+
+/* OTG device list */
+LIST_HEAD(otg_list);
+static DEFINE_MUTEX(otg_list_mutex);
+
+/* Hosts and Gadgets waiting for OTG controller */
+struct otg_wait_data {
+ struct device *dev; /* OTG controller device */
+
+ struct otg_hcd primary_hcd;
+ struct otg_hcd shared_hcd;
+ struct otg_gcd gcd;
+ struct list_head list;
+};
+
+LIST_HEAD(wait_list);
+static DEFINE_MUTEX(wait_list_mutex);
+
+static int usb_otg_hcd_is_primary_hcd(struct usb_hcd *hcd)
+{
+ if (!hcd->primary_hcd)
+ return 1;
+ return hcd == hcd->primary_hcd;
+}
+
+/**
+ * Check if the OTG device is in our wait list and return
+ * otg_wait_data, else NULL.
+ *
+ * wait_list_mutex must be held.
+ */
+static struct otg_wait_data *usb_otg_get_wait(struct device *otg_dev)
+{
+ struct otg_wait_data *wait;
+
+ if (!otg_dev)
+ return NULL;
+
+ /* is there an entry for this otg_dev ?*/
+ list_for_each_entry(wait, &wait_list, list) {
+ if (wait->dev == otg_dev)
+ return wait;
+ }
+
+ return NULL;
+}
+
+/**
+ * Add the hcd to our wait list
+ */
+static int usb_otg_hcd_wait_add(struct device *otg_dev, struct usb_hcd *hcd,
+ unsigned int irqnum, unsigned long irqflags,
+ struct otg_hcd_ops *ops)
+{
+ struct otg_wait_data *wait;
+ int ret = -EINVAL;
+
+ mutex_lock(&wait_list_mutex);
+
+ wait = usb_otg_get_wait(otg_dev);
+ if (!wait) {
+ /* Not yet in wait list? allocate and add */
+ wait = kzalloc(sizeof(*wait), GFP_KERNEL);
+ if (!wait) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ wait->dev = otg_dev;
+ list_add_tail(&wait->list, &wait_list);
+ }
+
+ if (usb_otg_hcd_is_primary_hcd(hcd)) {
+ if (wait->primary_hcd.hcd) /* already assigned? */
+ goto fail;
+
+ wait->primary_hcd.hcd = hcd;
+ wait->primary_hcd.irqnum = irqnum;
+ wait->primary_hcd.irqflags = irqflags;
+ wait->primary_hcd.ops = ops;
+ wait->primary_hcd.otg_dev = otg_dev;
+ } else {
+ if (wait->shared_hcd.hcd) /* already assigned? */
+ goto fail;
+
+ wait->shared_hcd.hcd = hcd;
+ wait->shared_hcd.irqnum = irqnum;
+ wait->shared_hcd.irqflags = irqflags;
+ wait->shared_hcd.ops = ops;
+ wait->shared_hcd.otg_dev = otg_dev;
+ }
+
+ mutex_unlock(&wait_list_mutex);
+ return 0;
+
+fail:
+ mutex_unlock(&wait_list_mutex);
+ return ret;
+}
+
+/**
+ * Check and free wait list entry if empty
+ *
+ * wait_list_mutex must be held
+ */
+static void usb_otg_check_free_wait(struct otg_wait_data *wait)
+{
+ if (wait->primary_hcd.hcd || wait->shared_hcd.hcd || wait->gcd.gadget)
+ return;
+
+ list_del(&wait->list);
+ kfree(wait);
+}
+
+/**
+ * Remove the hcd from our wait list
+ */
+static int usb_otg_hcd_wait_remove(struct usb_hcd *hcd)
+{
+ struct otg_wait_data *wait;
+
+ mutex_lock(&wait_list_mutex);
+
+ /* is there an entry for this hcd ?*/
+ list_for_each_entry(wait, &wait_list, list) {
+ if (wait->primary_hcd.hcd == hcd) {
+ wait->primary_hcd.hcd = 0;
+ goto found;
+ } else if (wait->shared_hcd.hcd == hcd) {
+ wait->shared_hcd.hcd = 0;
+ goto found;
+ }
+ }
+
+ mutex_unlock(&wait_list_mutex);
+ return -EINVAL;
+
+found:
+ usb_otg_check_free_wait(wait);
+ mutex_unlock(&wait_list_mutex);
+
+ return 0;
+}
+
+/**
+ * Add the gadget to our wait list
+ */
+static int usb_otg_gadget_wait_add(struct device *otg_dev,
+ struct usb_gadget *gadget,
+ struct otg_gadget_ops *ops)
+{
+ struct otg_wait_data *wait;
+ int ret = -EINVAL;
+
+ mutex_lock(&wait_list_mutex);
+
+ wait = usb_otg_get_wait(otg_dev);
+ if (!wait) {
+ /* Not yet in wait list? allocate and add */
+ wait = kzalloc(sizeof(*wait), GFP_KERNEL);
+ if (!wait) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ wait->dev = otg_dev;
+ list_add_tail(&wait->list, &wait_list);
+ }
+
+ if (wait->gcd.gadget) /* already assigned? */
+ goto fail;
+
+ wait->gcd.gadget = gadget;
+ wait->gcd.ops = ops;
+ mutex_unlock(&wait_list_mutex);
+
+ return 0;
+
+fail:
+ mutex_unlock(&wait_list_mutex);
+ return ret;
+}
+
+/**
+ * Remove the gadget from our wait list
+ */
+static int usb_otg_gadget_wait_remove(struct usb_gadget *gadget)
+{
+ struct otg_wait_data *wait;
+
+ mutex_lock(&wait_list_mutex);
+
+ /* is there an entry for this gadget ?*/
+ list_for_each_entry(wait, &wait_list, list) {
+ if (wait->gcd.gadget == gadget) {
+ wait->gcd.gadget = 0;
+ goto found;
+ }
+ }
+
+ mutex_unlock(&wait_list_mutex);
+
+ return -EINVAL;
+
+found:
+ usb_otg_check_free_wait(wait);
+ mutex_unlock(&wait_list_mutex);
+
+ return 0;
+}
+
+/**
+ * Register pending host/gadget and remove entry from wait list
+ */
+static void usb_otg_flush_wait(struct device *otg_dev)
+{
+ struct otg_wait_data *wait;
+ struct otg_hcd *host;
+ struct otg_gcd *gadget;
+
+ mutex_lock(&wait_list_mutex);
+
+ wait = usb_otg_get_wait(otg_dev);
+ if (!wait)
+ goto done;
+
+ dev_dbg(otg_dev, "otg: registering pending host/gadget\n");
+ gadget = &wait->gcd;
+ if (gadget)
+ usb_otg_register_gadget(gadget->gadget, gadget->ops);
+
+ host = &wait->primary_hcd;
+ if (host->hcd)
+ usb_otg_register_hcd(host->hcd, host->irqnum, host->irqflags,
+ host->ops);
+
+ host = &wait->shared_hcd;
+ if (host->hcd)
+ usb_otg_register_hcd(host->hcd, host->irqnum, host->irqflags,
+ host->ops);
+
+ list_del(&wait->list);
+ kfree(wait);
+
+done:
+ mutex_unlock(&wait_list_mutex);
+}
+
+/**
+ * Check if the OTG device is in our OTG list and return
+ * usb_otg data, else NULL.
+ *
+ * otg_list_mutex must be held.
+ */
+static struct usb_otg *usb_otg_get_data(struct device *otg_dev)
+{
+ struct usb_otg *otg;
+
+ if (!otg_dev)
+ return NULL;
+
+ list_for_each_entry(otg, &otg_list, list) {
+ if (otg->dev == otg_dev)
+ return otg;
+ }
+
+ return NULL;
+}
+
+/**
+ * usb_otg_start_host - start/stop the host controller
+ * @otg: usb_otg instance
+ * @on: true to start, false to stop
+ *
+ * Start/stop the USB host controller. This function is meant
+ * for use by the OTG controller driver.
+ */
+int usb_otg_start_host(struct usb_otg *otg, int on)
+{
+ struct otg_hcd_ops *hcd_ops = otg->hcd_ops;
+
+ dev_dbg(otg->dev, "otg: %s %d\n", __func__, on);
+ if (!otg->host) {
+ WARN_ONCE(1, "otg: fsm running without host\n");
+ return 0;
+ }
+
+ if (on) {
+ if (otg->flags & OTG_FLAG_HOST_RUNNING)
+ return 0;
+
+ otg->flags |= OTG_FLAG_HOST_RUNNING;
+
+ /* start host */
+ hcd_ops->add(otg->primary_hcd.hcd, otg->primary_hcd.irqnum,
+ otg->primary_hcd.irqflags);
+ if (otg->shared_hcd.hcd) {
+ hcd_ops->add(otg->shared_hcd.hcd,
+ otg->shared_hcd.irqnum,
+ otg->shared_hcd.irqflags);
+ }
+ } else {
+ if (!(otg->flags & OTG_FLAG_HOST_RUNNING))
+ return 0;
+
+ otg->flags &= ~OTG_FLAG_HOST_RUNNING;
+
+ /* stop host */
+ if (otg->shared_hcd.hcd)
+ hcd_ops->remove(otg->shared_hcd.hcd);
+
+ hcd_ops->remove(otg->primary_hcd.hcd);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usb_otg_start_host);
+
+/**
+ * usb_otg_start_gadget - start/stop the gadget controller
+ * @otg: usb_otg instance
+ * @on: true to start, false to stop
+ *
+ * Start/stop the USB gadget controller. This function is meant
+ * for use by the OTG controller driver.
+ */
+int usb_otg_start_gadget(struct usb_otg *otg, int on)
+{
+ struct usb_gadget *gadget = otg->gadget;
+
+ dev_dbg(otg->dev, "otg: %s %d\n", __func__, on);
+ if (!gadget) {
+ WARN_ONCE(1, "otg: fsm running without gadget\n");
+ return 0;
+ }
+
+ if (on) {
+ if (otg->flags & OTG_FLAG_GADGET_RUNNING)
+ return 0;
+
+ otg->flags |= OTG_FLAG_GADGET_RUNNING;
+ otg->gadget_ops->start(otg->gadget);
+ } else {
+ if (!(otg->flags & OTG_FLAG_GADGET_RUNNING))
+ return 0;
+
+ otg->flags &= ~OTG_FLAG_GADGET_RUNNING;
+ otg->gadget_ops->stop(otg->gadget);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usb_otg_start_gadget);
+
+/**
+ * Change USB protocol when there is a protocol change.
+ * fsm->lock must be held.
+ */
+static int drd_set_protocol(struct otg_fsm *fsm, int protocol)
+{
+ struct usb_otg *otg = container_of(fsm, struct usb_otg, fsm);
+ int ret = 0;
+
+ if (fsm->protocol != protocol) {
+ dev_dbg(otg->dev, "otg: changing role fsm->protocol= %d; new protocol= %d\n",
+ fsm->protocol, protocol);
+ /* stop old protocol */
+ if (fsm->protocol == PROTO_HOST)
+ ret = otg_start_host(otg, 0);
+ else if (fsm->protocol == PROTO_GADGET)
+ 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);
+ if (ret)
+ return ret;
+
+ fsm->protocol = protocol;
+ return 0;
+ }
+
+ return 0;
+}
+
+/**
+ * Called when entering a DRD state.
+ * fsm->lock must be held.
+ */
+static void drd_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state)
+{
+ struct usb_otg *otg = container_of(fsm, struct usb_otg, fsm);
+
+ if (otg->state == new_state)
+ return;
+
+ fsm->state_changed = 1;
+ dev_dbg(otg->dev, "otg: set state: %s\n",
+ usb_otg_state_string(new_state));
+ switch (new_state) {
+ case OTG_STATE_B_IDLE:
+ drd_set_protocol(fsm, PROTO_UNDEF);
+ otg_drv_vbus(otg, 0);
+ break;
+ case OTG_STATE_B_PERIPHERAL:
+ drd_set_protocol(fsm, PROTO_GADGET);
+ otg_drv_vbus(otg, 0);
+ break;
+ case OTG_STATE_A_HOST:
+ drd_set_protocol(fsm, PROTO_HOST);
+ otg_drv_vbus(otg, 1);
+ break;
+ 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(otg->dev, "%s: otg: invalid state: %s\n",
+ __func__, usb_otg_state_string(new_state));
+ break;
+ }
+
+ otg->state = new_state;
+}
+
+/**
+ * DRD state change judgement
+ *
+ * For DRD we're only interested in some of the OTG states
+ * i.e. OTG_STATE_B_IDLE: both peripheral and host are stopped
+ * OTG_STATE_B_PERIPHERAL: peripheral active
+ * OTG_STATE_A_HOST: host active
+ * we're only interested in the following inputs
+ * fsm->id, fsm->b_sess_vld
+ */
+int drd_statemachine(struct usb_otg *otg)
+{
+ struct otg_fsm *fsm = &otg->fsm;
+ enum usb_otg_state state;
+ int ret;
+
+ mutex_lock(&fsm->lock);
+
+ fsm->state_changed = 0;
+ state = otg->state;
+
+ switch (state) {
+ case OTG_STATE_UNDEFINED:
+ if (!fsm->id)
+ drd_set_state(fsm, OTG_STATE_A_HOST);
+ else if (fsm->id && fsm->b_sess_vld)
+ drd_set_state(fsm, OTG_STATE_B_PERIPHERAL);
+ else
+ drd_set_state(fsm, OTG_STATE_B_IDLE);
+ break;
+ case OTG_STATE_B_IDLE:
+ if (!fsm->id)
+ drd_set_state(fsm, OTG_STATE_A_HOST);
+ else if (fsm->b_sess_vld)
+ drd_set_state(fsm, OTG_STATE_B_PERIPHERAL);
+ break;
+ case OTG_STATE_B_PERIPHERAL:
+ if (!fsm->id)
+ drd_set_state(fsm, OTG_STATE_A_HOST);
+ else if (!fsm->b_sess_vld)
+ drd_set_state(fsm, OTG_STATE_B_IDLE);
+ break;
+ case OTG_STATE_A_HOST:
+ if (fsm->id && fsm->b_sess_vld)
+ drd_set_state(fsm, OTG_STATE_B_PERIPHERAL);
+ else if (fsm->id && !fsm->b_sess_vld)
+ drd_set_state(fsm, OTG_STATE_B_IDLE);
+ break;
+
+ /* 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(otg->dev, "%s: otg: invalid usb-drd state: %s\n",
+ __func__, usb_otg_state_string(state));
+ drd_set_state(fsm, OTG_STATE_UNDEFINED);
+ 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);
+
+/**
+ * OTG FSM/DRD work function
+ */
+static void usb_otg_work(struct work_struct *work)
+{
+ struct usb_otg *otg = container_of(work, struct usb_otg, work);
+
+ pm_runtime_get_sync(otg->dev);
+ drd_statemachine(otg);
+ pm_runtime_put_sync(otg->dev);
+}
+
+/**
+ * usb_otg_register() - Register the OTG/dual-role device to OTG core
+ * @dev: OTG/dual-role controller device.
+ * @config: OTG configuration.
+ *
+ * Registers the OTG/dual-role controller device with the USB OTG core.
+ *
+ * Return: struct usb_otg * if success, ERR_PTR() if error.
+ */
+struct usb_otg *usb_otg_register(struct device *dev,
+ struct usb_otg_config *config)
+{
+ struct usb_otg *otg;
+ struct otg_wait_data *wait;
+ int ret = 0;
+
+ if (!dev || !config || !config->fsm_ops)
+ return ERR_PTR(-EINVAL);
+
+ /* already in list? */
+ mutex_lock(&otg_list_mutex);
+ if (usb_otg_get_data(dev)) {
+ dev_err(dev, "otg: %s: device already in otg list\n",
+ __func__);
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ /* allocate and add to list */
+ otg = kzalloc(sizeof(*otg), GFP_KERNEL);
+ if (!otg) {
+ ret = -ENOMEM;
+ goto unlock;
+ }
+
+ otg->dev = dev;
+ otg->caps = config->otg_caps;
+
+ if ((otg->caps->hnp_support || otg->caps->srp_support ||
+ otg->caps->adp_support) && !config->otg_work)
+ dev_info(dev, "otg: limiting to dual-role\n");
+
+ if (config->otg_work) /* custom otg_work ? */
+ INIT_WORK(&otg->work, config->otg_work);
+ else
+ INIT_WORK(&otg->work, usb_otg_work);
+
+ otg->wq = create_singlethread_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);
+
+ /* were we in wait list? */
+ mutex_lock(&wait_list_mutex);
+ wait = usb_otg_get_wait(dev);
+ mutex_unlock(&wait_list_mutex);
+ if (wait) {
+ /* register pending host/gadget and flush from list */
+ usb_otg_flush_wait(dev);
+ }
+
+ return otg;
+
+err_wq:
+ kfree(otg);
+unlock:
+ mutex_unlock(&otg_list_mutex);
+ return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(usb_otg_register);
+
+/**
+ * usb_otg_unregister() - Unregister the OTG/dual-role device from USB OTG core
+ * @dev: OTG controller device.
+ *
+ * Unregisters the OTG/dual-role controller device from USB OTG core.
+ * Prevents unregistering till both the associated Host and Gadget controllers
+ * have unregistered from the OTG core.
+ *
+ * Return: 0 on success, error value otherwise.
+ */
+int usb_otg_unregister(struct device *dev)
+{
+ struct usb_otg *otg;
+
+ mutex_lock(&otg_list_mutex);
+ otg = usb_otg_get_data(dev);
+ if (!otg) {
+ dev_err(dev, "otg: %s: device not in otg list\n",
+ __func__);
+ mutex_unlock(&otg_list_mutex);
+ return -EINVAL;
+ }
+
+ /* prevent unregister till both host & gadget have unregistered */
+ if (otg->host || otg->gadget) {
+ dev_err(dev, "otg: %s: host/gadget still registered\n",
+ __func__);
+ return -EBUSY;
+ }
+
+ /* OTG FSM is halted when host/gadget unregistered */
+ destroy_workqueue(otg->wq);
+
+ /* remove from otg list */
+ list_del(&otg->list);
+ kfree(otg);
+ mutex_unlock(&otg_list_mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usb_otg_unregister);
+
+/**
+ * start/kick the OTG FSM if we can
+ * fsm->lock must be held
+ */
+static void usb_otg_start_fsm(struct usb_otg *otg)
+{
+ struct otg_fsm *fsm = &otg->fsm;
+
+ if (fsm->running)
+ goto kick_fsm;
+
+ if (!otg->host) {
+ dev_info(otg->dev, "otg: can't start till host registers\n");
+ return;
+ }
+
+ if (!otg->gadget) {
+ dev_info(otg->dev, "otg: can't start till gadget registers\n");
+ return;
+ }
+
+ fsm->running = true;
+kick_fsm:
+ queue_work(otg->wq, &otg->work);
+}
+
+/**
+ * stop the OTG FSM. Stops Host & Gadget controllers as well.
+ * fsm->lock must be held
+ */
+static void usb_otg_stop_fsm(struct usb_otg *otg)
+{
+ struct otg_fsm *fsm = &otg->fsm;
+
+ if (!fsm->running)
+ return;
+
+ /* no more new events queued */
+ fsm->running = false;
+
+ flush_workqueue(otg->wq);
+ otg->state = OTG_STATE_UNDEFINED;
+
+ /* stop host/gadget immediately */
+ if (fsm->protocol == PROTO_HOST)
+ otg_start_host(otg, 0);
+ else if (fsm->protocol == PROTO_GADGET)
+ otg_start_gadget(otg, 0);
+ fsm->protocol = PROTO_UNDEF;
+}
+
+/**
+ * usb_otg_sync_inputs - Sync OTG inputs with the OTG state machine
+ * @fsm: OTG FSM instance
+ *
+ * Used by the OTG driver to update the inputs to the OTG
+ * state machine.
+ *
+ * Can be called in IRQ context.
+ */
+void usb_otg_sync_inputs(struct usb_otg *otg)
+{
+ /* Don't kick FSM till it has started */
+ if (!otg->fsm.running)
+ return;
+
+ /* Kick FSM */
+ queue_work(otg->wq, &otg->work);
+}
+EXPORT_SYMBOL_GPL(usb_otg_sync_inputs);
+
+/**
+ * usb_otg_kick_fsm - Kick the OTG state machine
+ * @otg_dev: OTG controller device
+ *
+ * Used by USB host/device stack to sync OTG related
+ * events to the OTG state machine.
+ * e.g. change in host_bus->b_hnp_enable, gadget->b_hnp_enable
+ *
+ * Returns: 0 on success, error value otherwise.
+ */
+int usb_otg_kick_fsm(struct device *otg_dev)
+{
+ struct usb_otg *otg;
+
+ mutex_lock(&otg_list_mutex);
+ otg = usb_otg_get_data(otg_dev);
+ mutex_unlock(&otg_list_mutex);
+ if (!otg) {
+ dev_dbg(otg_dev, "otg: %s: invalid otg device\n",
+ __func__);
+ return -ENODEV;
+ }
+
+ usb_otg_sync_inputs(otg);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usb_otg_kick_fsm);
+
+/**
+ * usb_otg_register_hcd - Register the host controller to OTG core
+ * @hcd: host controller device
+ * @irqnum: interrupt number
+ * @irqflags: interrupt flags
+ * @ops: HCD ops to interface with the HCD
+ *
+ * This is used by the USB Host stack to register the host controller
+ * to the OTG core. Host controller must not be started by the
+ * caller as it is left upto the OTG state machine to do so.
+ * hcd->otg_dev must contain the related otg controller device.
+ *
+ * Returns: 0 on success, error value otherwise.
+ */
+int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
+ unsigned long irqflags, struct otg_hcd_ops *ops)
+{
+ struct usb_otg *otg;
+ struct device *hcd_dev = hcd->self.controller;
+ struct device *otg_dev = hcd->otg_dev;
+
+ if (!otg_dev)
+ return -EINVAL;
+
+ /* we're otg but otg controller might not yet be registered */
+ mutex_lock(&otg_list_mutex);
+ otg = usb_otg_get_data(otg_dev);
+ mutex_unlock(&otg_list_mutex);
+ if (!otg) {
+ dev_dbg(hcd_dev,
+ "otg: controller not yet registered. waiting..\n");
+ /*
+ * otg controller might register later. Put the hcd in
+ * wait list and call us back when ready
+ */
+ if (usb_otg_hcd_wait_add(otg_dev, hcd, irqnum, irqflags, ops)) {
+ dev_err(hcd_dev, "otg: failed to add hcd to wait list\n");
+ return -EINVAL;
+ }
+
+ return 0;
+ }
+
+ /* 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) {
+ /* are we in wait list? */
+ if (!usb_otg_hcd_wait_remove(hcd))
+ return 0;
+
+ dev_dbg(hcd_dev, "otg: host wasn't registered with otg\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&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 {
+ dev_err(otg_dev, "otg: host %s wasn't registered with otg\n",
+ dev_name(hcd_dev));
+ mutex_unlock(&otg->fsm.lock);
+ return -EINVAL;
+ }
+
+ /* stop FSM & Host */
+ usb_otg_stop_fsm(otg);
+ otg->host = NULL;
+
+ mutex_unlock(&otg->fsm.lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usb_otg_unregister_hcd);
+
+/**
+ * usb_otg_register_gadget - Register the gadget controller to OTG core
+ * @gadget: gadget controller
+ *
+ * This is used by the USB gadget stack to register the gadget controller
+ * to the OTG core. Gadget controller must not be started by the
+ * caller as it is left upto the OTG state machine to do so.
+ *
+ * Gadget core must call this only when all resources required for
+ * gadget controller to run are available.
+ * i.e. gadget function driver is available.
+ *
+ * Returns: 0 on success, error value otherwise.
+ */
+int usb_otg_register_gadget(struct usb_gadget *gadget,
+ struct otg_gadget_ops *ops)
+{
+ struct usb_otg *otg;
+ struct device *gadget_dev = &gadget->dev;
+ struct device *otg_dev = gadget->otg_dev;
+
+ if (!otg_dev)
+ return -EINVAL; /* we're definitely not OTG */
+
+ /* we're otg but otg controller might not yet be registered */
+ mutex_lock(&otg_list_mutex);
+ otg = usb_otg_get_data(otg_dev);
+ mutex_unlock(&otg_list_mutex);
+ if (!otg) {
+ dev_dbg(gadget_dev,
+ "otg: controller not yet registered. waiting..\n");
+ /*
+ * otg controller might register later. Put the gadget in
+ * wait list and call us back when ready
+ */
+ if (usb_otg_gadget_wait_add(otg_dev, gadget, ops)) {
+ dev_err(gadget_dev,
+ "otg: failed to add to gadget to wait list\n");
+ return -EINVAL;
+ }
+
+ return 0;
+ }
+
+ 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));
+
+ /* start FSM */
+ usb_otg_start_fsm(otg);
+ mutex_unlock(&otg->fsm.lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usb_otg_register_gadget);
+
+/**
+ * usb_otg_unregister_gadget - Unregister the gadget controller from OTG core
+ * @gadget: gadget controller
+ *
+ * This is used by the USB gadget stack to unregister the gadget controller
+ * from the OTG core. Ensures that gadget controller is halted
+ * on successful return.
+ *
+ * Returns: 0 on success, error value otherwise.
+ */
+int usb_otg_unregister_gadget(struct usb_gadget *gadget)
+{
+ struct usb_otg *otg;
+ struct device *gadget_dev = &gadget->dev;
+ struct device *otg_dev = gadget->otg_dev;
+
+ if (!otg_dev)
+ return -EINVAL;
+
+ mutex_lock(&otg_list_mutex);
+ otg = usb_otg_get_data(otg_dev);
+ mutex_unlock(&otg_list_mutex);
+ if (!otg) {
+ /* are we in wait list? */
+ if (!usb_otg_gadget_wait_remove(gadget))
+ return 0;
+
+ dev_dbg(gadget_dev, "otg: gadget wasn't registered with otg\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&otg->fsm.lock);
+ if (otg->gadget != gadget) {
+ dev_err(otg_dev, "otg: gadget %s wasn't registered with otg\n",
+ dev_name(&gadget->dev));
+ mutex_unlock(&otg->fsm.lock);
+ return -EINVAL;
+ }
+
+ /* Stop FSM & gadget */
+ 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);
diff --git a/drivers/usb/common/usb-otg.h b/drivers/usb/common/usb-otg.h
new file mode 100644
index 0000000..2bf3fbf
--- /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) 2016 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 ae228d0..b468a9f 100644
--- a/drivers/usb/core/Kconfig
+++ b/drivers/usb/core/Kconfig
@@ -42,7 +42,7 @@ config USB_DYNAMIC_MINORS
If you are unsure about this, say N here.

config USB_OTG
- bool "OTG support"
+ bool "OTG/Dual-role support"
depends on PM
default n
help
diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h
index 8c0ae64..1878ae1 100644
--- a/include/linux/usb/gadget.h
+++ b/include/linux/usb/gadget.h
@@ -583,6 +583,7 @@ struct usb_gadget_ops {
* @out_epnum: last used out ep number
* @in_epnum: last used in ep number
* @otg_caps: OTG capabilities of this gadget.
+ * @otg_dev: OTG controller device, if needs to be used with OTG core.
* @sg_supported: true if we can handle scatter-gather
* @is_otg: True if the USB device port uses a Mini-AB jack, so that the
* gadget driver must provide a USB OTG descriptor.
@@ -639,6 +640,7 @@ struct usb_gadget {
unsigned out_epnum;
unsigned in_epnum;
struct usb_otg_caps *otg_caps;
+ struct device *otg_dev;

unsigned sg_supported:1;
unsigned is_otg:1;
diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h
index 861ccaa..2017cd4 100644
--- a/include/linux/usb/hcd.h
+++ b/include/linux/usb/hcd.h
@@ -184,6 +184,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 36f0cf9..ba6755c 100644
--- a/include/linux/usb/otg-fsm.h
+++ b/include/linux/usb/otg-fsm.h
@@ -61,6 +61,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
@@ -133,6 +138,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 */
@@ -188,6 +194,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..b094352 100644
--- a/include/linux/usb/otg.h
+++ b/include/linux/usb/otg.h
@@ -10,10 +10,55 @@
#define __LINUX_USB_OTG_H

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

+/**
+ * struct otg_hcd - host controller state and interface
+ *
+ * @hcd: host controller
+ * @irqnum: irq number
+ * @irqflags: irq flags
+ * @ops: otg to host controller interface
+ * @ops: otg to host controller interface
+ * @otg_dev: otg controller device
+ */
+struct otg_hcd {
+ struct usb_hcd *hcd;
+ unsigned int irqnum;
+ unsigned long irqflags;
+ struct otg_hcd_ops *ops;
+ struct device *otg_dev;
+};
+
+/**
+ * struct usb_otg - 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 +69,24 @@ 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;
+ 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,7 +102,7 @@ struct usb_otg {

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

/**
@@ -60,8 +120,92 @@ struct usb_otg_caps {
bool adp_support;
};

+/**
+ * struct usb_otg_config - otg controller configuration
+ * @caps: otg capabilities of the controller
+ * @ops: otg fsm operations
+ * @otg_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);
+};
+
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);
+
+#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;
+}
+#endif /* CONFIG_USB_OTG */
+
+/*------------- deprecated interface -----------------------------*/
/* Context: can sleep */
static inline int
otg_start_hnp(struct usb_otg *otg)
@@ -113,6 +257,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 +383,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.5.0

2016-04-05 14:08:46

by Roger Quadros

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

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

Introduce usb_otg_add_gadget_udc() to allow controller drivers
to register a gadget controller that is part of an OTG instance.

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

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

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

Signed-off-by: Roger Quadros <[email protected]>
---
drivers/usb/gadget/udc/udc-core.c | 166 +++++++++++++++++++++++++++++++++++---
include/linux/usb/gadget.h | 4 +
2 files changed, 161 insertions(+), 9 deletions(-)

diff --git a/drivers/usb/gadget/udc/udc-core.c b/drivers/usb/gadget/udc/udc-core.c
index 4151597..9b9702f 100644
--- a/drivers/usb/gadget/udc/udc-core.c
+++ b/drivers/usb/gadget/udc/udc-core.c
@@ -28,6 +28,10 @@
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
#include <linux/usb.h>
+#include <linux/usb/otg.h>
+
+#include <linux/of.h>
+#include <linux/of_platform.h>

/**
* struct usb_udc - describes one usb device controller
@@ -304,6 +308,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);
}

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

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

+/**
+ * usb_otg_add_gadget_udc - adds a new gadget to the udc class driver list
+ * @parent: the parent device to this udc. Usually the controller
+ * driver's device.
+ * @gadget: the gadget to be added to the list
+ * @otg_dev: the OTG controller device
+ *
+ * If otg_dev is NULL then device tree node is checked
+ * for OTG controller via the otg-controller property.
+ * Returns zero on success, negative errno otherwise.
+ */
+int usb_otg_add_gadget_udc(struct device *parent, struct usb_gadget *gadget,
+ struct device *otg_dev)
+{
+ if (!otg_dev) {
+ struct device_node *np;
+ struct platform_device *pdev;
+
+ np = of_parse_phandle(parent->of_node, "otg-controller", 0);
+ if (!np) {
+ dev_err(parent,
+ "otg_dev is NULL or no otg-controller property in DT\n");
+ return -EINVAL;
+ }
+
+ pdev = of_find_device_by_node(np);
+ of_node_put(np);
+ if (!pdev) {
+ dev_err(parent, "couldn't get otg-controller device\n");
+ return -ENODEV;
+ }
+
+ gadget->otg_dev = &pdev->dev;
+ } else {
+ gadget->otg_dev = otg_dev;
+ }
+
+ return usb_add_gadget_udc_release(parent, gadget, NULL);
+}
+EXPORT_SYMBOL_GPL(usb_otg_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",
@@ -493,10 +611,18 @@ static void usb_gadget_remove_driver(struct usb_udc *udc)

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

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

udc->driver = NULL;
udc->dev.driver = NULL;
@@ -530,6 +656,8 @@ void usb_del_gadget_udc(struct usb_gadget *gadget)
}
mutex_unlock(&udc_lock);

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

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

+struct otg_gadget_ops otg_gadget_intf = {
+ .start = usb_gadget_start,
+ .stop = usb_gadget_stop,
+};
+
+/* udc_lock must be held */
static int udc_bind_to_driver(struct usb_udc *udc, struct usb_gadget_driver *driver)
{
int ret;
@@ -553,12 +687,20 @@ 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 */
+ if (udc->gadget->otg_dev) {
+ mutex_unlock(&udc_lock);
+ usb_otg_register_gadget(udc->gadget, &otg_gadget_intf);
+ mutex_lock(&udc_lock);
+ } else {
+ ret = usb_gadget_udc_start(udc);
+ if (ret) {
+ driver->unbind(udc->gadget);
+ goto err1;
+ }
+ usb_udc_connect_control(udc);
}
- usb_udc_connect_control(udc);

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

+ /* In OTG mode we don't support softconnect, but b_bus_req */
+ if (udc->gadget->otg_dev) {
+ dev_err(dev, "soft-connect not supported in OTG mode\n");
+ return -EOPNOTSUPP;
+ }
+
if (sysfs_streq(buf, "connect")) {
usb_gadget_udc_start(udc);
- usb_gadget_connect(udc->gadget);
+ usb_udc_connect_control(udc);
} else if (sysfs_streq(buf, "disconnect")) {
usb_gadget_disconnect(udc->gadget);
udc->driver->disconnect(udc->gadget);
diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h
index 1878ae1..061f09e4 100644
--- a/include/linux/usb/gadget.h
+++ b/include/linux/usb/gadget.h
@@ -1160,6 +1160,10 @@ extern int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget);
extern void usb_del_gadget_udc(struct usb_gadget *gadget);
extern char *usb_get_gadget_udc_name(void);

+extern int usb_otg_add_gadget_udc(struct device *parent,
+ struct usb_gadget *gadget,
+ struct device *otg_dev);
+
/*-------------------------------------------------------------------------*/

/* utility to simplify dealing with string descriptors */
--
2.5.0

2016-04-05 14:06:18

by Roger Quadros

[permalink] [raw]
Subject: [PATCH v6 06/12] usb: otg: get rid of CONFIG_USB_OTG_FSM in favour of CONFIG_USB_OTG

Let's use CONFIG_USB_OTG as a single config option to enable
USB OTG and the OTG FSM. This makes things a lot less confusing.

Update all users of CONFIG_USB_OTG_FSM to CONFIG_USB_OTG.

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

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

1.1 How to enable OTG FSM in menuconfig
---------------------------------------
-Select CONFIG_USB_OTG_FSM, rebuild kernel Image and modules.
+Select CONFIG_USB_OTG, rebuild kernel Image and modules.
If you want to check some internal variables for otg fsm,
mount debugfs, there are 2 files which can show otg fsm
variables and some controller registers value:
diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
index dca7856..16a5b55 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) += common/

obj-$(CONFIG_USBIP_CORE) += usbip/
diff --git a/drivers/usb/chipidea/Makefile b/drivers/usb/chipidea/Makefile
index 518e445..45aa24d 100644
--- a/drivers/usb/chipidea/Makefile
+++ b/drivers/usb/chipidea/Makefile
@@ -3,7 +3,7 @@ obj-$(CONFIG_USB_CHIPIDEA) += ci_hdrc.o
ci_hdrc-y := core.o otg.o debug.o
ci_hdrc-$(CONFIG_USB_CHIPIDEA_UDC) += udc.o
ci_hdrc-$(CONFIG_USB_CHIPIDEA_HOST) += host.o
-ci_hdrc-$(CONFIG_USB_OTG_FSM) += otg_fsm.o
+ci_hdrc-$(CONFIG_USB_OTG) += otg_fsm.o

# Glue/Bridge layers go here

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

return ci->is_otg && ci->roles[CI_ROLE_HOST] &&
diff --git a/drivers/usb/chipidea/otg_fsm.h b/drivers/usb/chipidea/otg_fsm.h
index 6366fe3..2d451bb 100644
--- a/drivers/usb/chipidea/otg_fsm.h
+++ b/drivers/usb/chipidea/otg_fsm.h
@@ -64,7 +64,7 @@

#define TB_AIDL_BDIS (20) /* 4ms ~ 150ms, section 5.2.1 */

-#if IS_ENABLED(CONFIG_USB_OTG_FSM)
+#if IS_ENABLED(CONFIG_USB_OTG)

int ci_hdrc_otg_fsm_init(struct ci_hdrc *ci);
int ci_otg_fsm_work(struct ci_hdrc *ci);
diff --git a/drivers/usb/common/Makefile b/drivers/usb/common/Makefile
index 6bbb3ec..f8f2c88 100644
--- a/drivers/usb/common/Makefile
+++ b/drivers/usb/common/Makefile
@@ -6,5 +6,6 @@ obj-$(CONFIG_USB_COMMON) += usb-common.o
usb-common-y += common.o
usb-common-$(CONFIG_USB_LED_TRIG) += led.o

-obj-$(CONFIG_USB_OTG_FSM) += usb-otg-fsm.o
obj-$(CONFIG_USB_ULPI_BUS) += ulpi.o
+usbotg-y := usb-otg-fsm.o
+obj-$(CONFIG_USB_OTG) += usbotg.o
diff --git a/drivers/usb/core/Kconfig b/drivers/usb/core/Kconfig
index dd28010..ae228d0 100644
--- a/drivers/usb/core/Kconfig
+++ b/drivers/usb/core/Kconfig
@@ -75,14 +75,6 @@ config USB_OTG_BLACKLIST_HUB
and software costs by not supporting external hubs. So
are "Embedded Hosts" that don't offer OTG support.

-config USB_OTG_FSM
- tristate "USB 2.0 OTG FSM implementation"
- depends on USB && USB_OTG
- select USB_PHY
- help
- Implements OTG Finite State Machine as specified in On-The-Go
- and Embedded Host Supplement to the USB Revision 2.0 Specification.
-
config USB_ULPI_BUS
tristate "USB ULPI PHY interface support"
depends on USB_SUPPORT
diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig
index c690474..06794e2 100644
--- a/drivers/usb/phy/Kconfig
+++ b/drivers/usb/phy/Kconfig
@@ -20,7 +20,7 @@ config AB8500_USB

config FSL_USB2_OTG
bool "Freescale USB OTG Transceiver Driver"
- depends on USB_EHCI_FSL && USB_FSL_USB2 && USB_OTG_FSM && PM
+ depends on USB_EHCI_FSL && USB_FSL_USB2 && USB_OTG && PM
select USB_PHY
help
Enable this to support Freescale USB OTG transceiver.
--
2.5.0

2016-04-05 14:09:39

by Roger Quadros

[permalink] [raw]
Subject: [PATCH v6 10/12] usb: doc: dt-binding: Add otg-controller property

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

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

Signed-off-by: Roger Quadros <[email protected]>
---
Documentation/devicetree/bindings/usb/generic.txt | 3 +++
1 file changed, 3 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:

--
2.5.0

2016-04-05 14:06:15

by Roger Quadros

[permalink] [raw]
Subject: [PATCH v6 08/12] usb: hcd: Adapt to OTG core

Introduce usb_otg_add/remove_hcd() for use by host
controllers that are part of OTG/dual-role port.

Non Device tree platforms can use the otg_dev argument
to specify the OTG controller device. If otg_dev is NULL
then the device tree node's otg-controller property is used to
get the otg_dev device.

Signed-off-by: Roger Quadros <[email protected]>
---
drivers/usb/core/hcd.c | 71 +++++++++++++++++++++++++++++++++++++++++++++++++
include/linux/usb/hcd.h | 4 +++
2 files changed, 75 insertions(+)

diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index 6b1930d..6a80193 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -46,6 +46,10 @@
#include <linux/usb.h>
#include <linux/usb/hcd.h>
#include <linux/usb/phy.h>
+#include <linux/usb/otg.h>
+
+#include <linux/of.h>
+#include <linux/of_platform.h>

#include "usb.h"

@@ -3013,6 +3017,73 @@ void usb_remove_hcd(struct usb_hcd *hcd)
}
EXPORT_SYMBOL_GPL(usb_remove_hcd);

+
+static struct otg_hcd_ops otg_hcd_intf = {
+ .add = usb_add_hcd,
+ .remove = usb_remove_hcd,
+ .usb_bus_start_enum = usb_bus_start_enum,
+ .usb_control_msg = usb_control_msg,
+ .usb_hub_find_child = usb_hub_find_child,
+};
+
+/**
+ * usb_otg_add_hcd - Register the HCD with OTG core.
+ * @hcd: the usb_hcd structure to initialize
+ * @irqnum: Interrupt line to allocate
+ * @irqflags: Interrupt type flags
+ * @otg_dev: OTG controller device manging this HCD
+ *
+ * Registers the HCD with OTG core. OTG core will call usb_add_hcd()
+ * or usb_remove_hcd() as necessary.
+ * If otg_dev is NULL then device tree node is checked for OTG
+ * controller device via the otg-controller property.
+ */
+int usb_otg_add_hcd(struct usb_hcd *hcd,
+ unsigned int irqnum, unsigned long irqflags,
+ struct device *otg_dev)
+{
+ struct device *dev = hcd->self.controller;
+
+ if (!otg_dev) {
+ struct device_node *np;
+ struct platform_device *pdev;
+
+ np = of_parse_phandle(dev->of_node, "otg-controller", 0);
+ if (!np) {
+ dev_err(dev,
+ "otg_dev is NULL and no otg-controller property in DT\n");
+ return -EINVAL;
+ }
+
+ pdev = of_find_device_by_node(np);
+ of_node_put(np);
+ if (!pdev) {
+ dev_err(dev,
+ "couldn't get otg-controller device\n");
+ return -ENODEV;
+ }
+
+ hcd->otg_dev = &pdev->dev;
+ } else {
+ hcd->otg_dev = otg_dev;
+ }
+
+ return usb_otg_register_hcd(hcd, irqnum, irqflags, &otg_hcd_intf);
+}
+EXPORT_SYMBOL_GPL(usb_otg_add_hcd);
+
+/**
+ * usb_otg_remove_hcd - Unregister the HCD with OTG core.
+ * @hcd: the usb_hcd structure to remove
+ *
+ * Unregisters the HCD from the OTG core.
+ */
+void usb_otg_remove_hcd(struct usb_hcd *hcd)
+{
+ usb_otg_unregister_hcd(hcd);
+}
+EXPORT_SYMBOL_GPL(usb_otg_remove_hcd);
+
void
usb_hcd_platform_shutdown(struct platform_device *dev)
{
diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h
index 2017cd4..adcf2e7 100644
--- a/include/linux/usb/hcd.h
+++ b/include/linux/usb/hcd.h
@@ -472,6 +472,10 @@ 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 void usb_remove_hcd(struct usb_hcd *hcd);
+extern int usb_otg_add_hcd(struct usb_hcd *hcd,
+ unsigned int irqnum, unsigned long irqflags,
+ struct device *otg_dev);
+extern void usb_otg_remove_hcd(struct usb_hcd *hcd);
extern int usb_hcd_find_raw_port_number(struct usb_hcd *hcd, int port1);

struct platform_device;
--
2.5.0

2016-04-06 03:23:51

by Yoshihiro Shimoda

[permalink] [raw]
Subject: RE: [PATCH v6 12/12] usb: host: xhci-plat: Add otg device to platform data

Hi,

> Sent: Tuesday, April 05, 2016 11:05 PM
>
> Host controllers that are part of an OTG/dual-role instance
> need to somehow pass the OTG controller device information
> to the HCD core.
>
> We use platform data to pass the OTG controller device.
>
> Signed-off-by: Roger Quadros <[email protected]>
> ---
> drivers/usb/host/xhci-plat.c | 35 ++++++++++++++++++++++++++++-------
> include/linux/usb/xhci_pdriver.h | 3 +++
> 2 files changed, 31 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c
> index 5c15e9b..3d0f597 100644
> --- a/drivers/usb/host/xhci-plat.c
> +++ b/drivers/usb/host/xhci-plat.c
> @@ -230,11 +230,20 @@ static int xhci_plat_probe(struct platform_device *pdev)
> goto put_usb3_hcd;
> }
>
> - ret = usb_add_hcd(hcd, irq, IRQF_SHARED);
> + if (pdata->otg_dev)

I think that this should be changed like below:
if (pdata && pdata->otg_dev)

Otherwise, panic happened on my environment (arm64 / r8a7795 board) because the pdata was NULL.

> + ret = usb_otg_add_hcd(hcd, irq, IRQF_SHARED, pdata->otg_dev);
> + else
> + ret = usb_add_hcd(hcd, irq, IRQF_SHARED);
> +
> if (ret)
> goto disable_usb_phy;
>
> - ret = usb_add_hcd(xhci->shared_hcd, irq, IRQF_SHARED);
> + if (pdata->otg_dev)

Ditto.

> + ret = usb_otg_add_hcd(xhci->shared_hcd, irq, IRQF_SHARED,
> + pdata->otg_dev);
> + else
> + ret = usb_add_hcd(xhci->shared_hcd, irq, IRQF_SHARED);
> +
> if (ret)
> goto dealloc_usb2_hcd;
>
> @@ -242,7 +251,10 @@ static int xhci_plat_probe(struct platform_device *pdev)
>
>
> dealloc_usb2_hcd:
> - usb_remove_hcd(hcd);
> + if (pdata->otg_dev)

Ditto.

> + usb_otg_remove_hcd(hcd);
> + else
> + usb_remove_hcd(hcd);
>
> disable_usb_phy:
> usb_phy_shutdown(hcd->usb_phy);
> @@ -260,16 +272,25 @@ put_hcd:
> return ret;
> }
>
> -static int xhci_plat_remove(struct platform_device *dev)
> +static int xhci_plat_remove(struct platform_device *pdev)
> {
> - struct usb_hcd *hcd = platform_get_drvdata(dev);
> + struct usb_hcd *hcd = platform_get_drvdata(pdev);
> struct xhci_hcd *xhci = hcd_to_xhci(hcd);
> struct clk *clk = xhci->clk;
> + struct usb_xhci_pdata *pdata = dev_get_platdata(&pdev->dev);
> +
> + if (pdata->otg_dev)

Ditto.

> + usb_otg_remove_hcd(xhci->shared_hcd);
> + else
> + usb_remove_hcd(xhci->shared_hcd);
>
> - usb_remove_hcd(xhci->shared_hcd);
> usb_phy_shutdown(hcd->usb_phy);
>
> - usb_remove_hcd(hcd);
> + if (pdata->otg_dev)

Ditto.

Best regards,
Yoshihiro Shimoda

> + usb_otg_remove_hcd(hcd);
> + else
> + usb_remove_hcd(hcd);
> +
> usb_put_hcd(xhci->shared_hcd);
>
> if (!IS_ERR(clk))
> diff --git a/include/linux/usb/xhci_pdriver.h b/include/linux/usb/xhci_pdriver.h
> index 376654b..5c68b83 100644
> --- a/include/linux/usb/xhci_pdriver.h
> +++ b/include/linux/usb/xhci_pdriver.h
> @@ -18,10 +18,13 @@
> *
> * @usb3_lpm_capable: determines if this xhci platform supports USB3
> * LPM capability
> + * @otg_dev: OTG controller device. Only requied if part of
> + * OTG/dual-role.
> *
> */
> struct usb_xhci_pdata {
> unsigned usb3_lpm_capable:1;
> + struct device *otg_dev;
> };
>
> #endif /* __USB_CORE_XHCI_PDRIVER_H */
> --
> 2.5.0
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-usb" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html

2016-04-06 06:11:11

by Felipe Balbi

[permalink] [raw]
Subject: Re: [PATCH v6 01/12] usb: hcd: Initialize hcd->flags to 0


Hi,

Roger Quadros <[email protected]> writes:
> diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
> index 2ca2cef..6b1930d 100644
> --- a/drivers/usb/core/hcd.c
> +++ b/drivers/usb/core/hcd.c
> @@ -2706,6 +2706,7 @@ int usb_add_hcd(struct usb_hcd *hcd,
> int retval;
> struct usb_device *rhdev;
>
> + hcd->flags = 0;

seems like this would make more sense in usb_del_hcd() instead.

--
balbi


Attachments:
signature.asc (818.00 B)

2016-04-06 06:30:37

by Roger Quadros

[permalink] [raw]
Subject: Re: [PATCH v6 12/12] usb: host: xhci-plat: Add otg device to platform data

Hi,

On 06/04/16 06:23, Yoshihiro Shimoda wrote:
> Hi,
>
>> Sent: Tuesday, April 05, 2016 11:05 PM
>>
>> Host controllers that are part of an OTG/dual-role instance
>> need to somehow pass the OTG controller device information
>> to the HCD core.
>>
>> We use platform data to pass the OTG controller device.
>>
>> Signed-off-by: Roger Quadros <[email protected]>
>> ---
>> drivers/usb/host/xhci-plat.c | 35 ++++++++++++++++++++++++++++-------
>> include/linux/usb/xhci_pdriver.h | 3 +++
>> 2 files changed, 31 insertions(+), 7 deletions(-)
>>
>> diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c
>> index 5c15e9b..3d0f597 100644
>> --- a/drivers/usb/host/xhci-plat.c
>> +++ b/drivers/usb/host/xhci-plat.c
>> @@ -230,11 +230,20 @@ static int xhci_plat_probe(struct platform_device *pdev)
>> goto put_usb3_hcd;
>> }
>>
>> - ret = usb_add_hcd(hcd, irq, IRQF_SHARED);
>> + if (pdata->otg_dev)
>
> I think that this should be changed like below:
> if (pdata && pdata->otg_dev)

Indeed, I'll fix this and the remaining occurrences. Thanks.

cheers,
-roger

>
> Otherwise, panic happened on my environment (arm64 / r8a7795 board) because the pdata was NULL.
>
>> + ret = usb_otg_add_hcd(hcd, irq, IRQF_SHARED, pdata->otg_dev);
>> + else
>> + ret = usb_add_hcd(hcd, irq, IRQF_SHARED);
>> +
>> if (ret)
>> goto disable_usb_phy;
>>
>> - ret = usb_add_hcd(xhci->shared_hcd, irq, IRQF_SHARED);
>> + if (pdata->otg_dev)
>
> Ditto.
>
>> + ret = usb_otg_add_hcd(xhci->shared_hcd, irq, IRQF_SHARED,
>> + pdata->otg_dev);
>> + else
>> + ret = usb_add_hcd(xhci->shared_hcd, irq, IRQF_SHARED);
>> +
>> if (ret)
>> goto dealloc_usb2_hcd;
>>
>> @@ -242,7 +251,10 @@ static int xhci_plat_probe(struct platform_device *pdev)
>>
>>
>> dealloc_usb2_hcd:
>> - usb_remove_hcd(hcd);
>> + if (pdata->otg_dev)
>
> Ditto.
>
>> + usb_otg_remove_hcd(hcd);
>> + else
>> + usb_remove_hcd(hcd);
>>
>> disable_usb_phy:
>> usb_phy_shutdown(hcd->usb_phy);
>> @@ -260,16 +272,25 @@ put_hcd:
>> return ret;
>> }
>>
>> -static int xhci_plat_remove(struct platform_device *dev)
>> +static int xhci_plat_remove(struct platform_device *pdev)
>> {
>> - struct usb_hcd *hcd = platform_get_drvdata(dev);
>> + struct usb_hcd *hcd = platform_get_drvdata(pdev);
>> struct xhci_hcd *xhci = hcd_to_xhci(hcd);
>> struct clk *clk = xhci->clk;
>> + struct usb_xhci_pdata *pdata = dev_get_platdata(&pdev->dev);
>> +
>> + if (pdata->otg_dev)
>
> Ditto.
>
>> + usb_otg_remove_hcd(xhci->shared_hcd);
>> + else
>> + usb_remove_hcd(xhci->shared_hcd);
>>
>> - usb_remove_hcd(xhci->shared_hcd);
>> usb_phy_shutdown(hcd->usb_phy);
>>
>> - usb_remove_hcd(hcd);
>> + if (pdata->otg_dev)
>
> Ditto.
>
> Best regards,
> Yoshihiro Shimoda
>
>> + usb_otg_remove_hcd(hcd);
>> + else
>> + usb_remove_hcd(hcd);
>> +
>> usb_put_hcd(xhci->shared_hcd);
>>
>> if (!IS_ERR(clk))
>> diff --git a/include/linux/usb/xhci_pdriver.h b/include/linux/usb/xhci_pdriver.h
>> index 376654b..5c68b83 100644
>> --- a/include/linux/usb/xhci_pdriver.h
>> +++ b/include/linux/usb/xhci_pdriver.h
>> @@ -18,10 +18,13 @@
>> *
>> * @usb3_lpm_capable: determines if this xhci platform supports USB3
>> * LPM capability
>> + * @otg_dev: OTG controller device. Only requied if part of
>> + * OTG/dual-role.
>> *
>> */
>> struct usb_xhci_pdata {
>> unsigned usb3_lpm_capable:1;
>> + struct device *otg_dev;
>> };
>>
>> #endif /* __USB_CORE_XHCI_PDRIVER_H */
>> --
>> 2.5.0
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-usb" in
>> the body of a message to [email protected]
>> More majordomo info at http://vger.kernel.org/majordomo-info.html

2016-04-06 06:32:40

by Roger Quadros

[permalink] [raw]
Subject: Re: [PATCH v6 01/12] usb: hcd: Initialize hcd->flags to 0

On 06/04/16 09:09, Felipe Balbi wrote:
>
> Hi,
>
> Roger Quadros <[email protected]> writes:
>> diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
>> index 2ca2cef..6b1930d 100644
>> --- a/drivers/usb/core/hcd.c
>> +++ b/drivers/usb/core/hcd.c
>> @@ -2706,6 +2706,7 @@ int usb_add_hcd(struct usb_hcd *hcd,
>> int retval;
>> struct usb_device *rhdev;
>>
>> + hcd->flags = 0;
>
> seems like this would make more sense in usb_del_hcd() instead.
>

OK, I'll move it there.

cheers,
-roger

2016-04-07 08:52:36

by Yoshihiro Shimoda

[permalink] [raw]
Subject: RE: [PATCH v6 07/12] usb: otg: add OTG/dual-role core

Hi,

> From: Roger Quadros
> Sent: Tuesday, April 05, 2016 11:05 PM
>
> 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 <[email protected]>
> ---

I tried to implement this framework on my environment,
and it seemed work if I added a local hack to this patch :)

< snip >
> + /* 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");

My environment is arm64 / r8a7795, renesas_usbhs (as a gadget), EHCI and OHCI.
In this case, OHCI driver is also a primary_hcd. So, this error happened.
To avoid this, I assumes that the OHCI hcd is as shared_hcd like a patch below:

diff --git a/drivers/usb/common/usb-otg.c b/drivers/usb/common/usb-otg.c
index 41e762a..4d7f043 100644
--- a/drivers/usb/common/usb-otg.c
+++ b/drivers/usb/common/usb-otg.c
@@ -825,11 +825,16 @@ int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
if (otg->primary_hcd.hcd) {
/* probably a shared HCD ? */
if (usb_otg_hcd_is_primary_hcd(hcd)) {
+ if (hcd->driver->flags & HCD_USB11) {
+ dev_info(otg_dev, "this assumes usb 1.1 hc is as shared_hcd\n");
+ goto check_shared_hcd;
+ }
dev_err(otg_dev, "otg: primary host already registered\n");
goto err;
}

if (hcd->shared_hcd == otg->primary_hcd.hcd) {
+check_shared_hcd:
if (otg->shared_hcd.hcd) {
dev_err(otg_dev, "otg: shared host already registered\n");
goto err;

What do you think this local hack?
I also wonder if array of hcd may be good for both xHCI and EHCI/OHCI.
For example of xHCI:
- otg->hcds[0] = primary_hcd
- otg->hcds[1] = shared_hcd

For example of EHCI/OHCI:
- otg->hcds[0] = primary_hcd of EHCI
- otg->hcds[1] = primary_hcd of OHCI

Best regards,
Yoshihiro Shimoda

2016-04-07 09:49:35

by Peter Chen

[permalink] [raw]
Subject: Re: [PATCH v6 01/12] usb: hcd: Initialize hcd->flags to 0

On Wed, Apr 06, 2016 at 09:32:22AM +0300, Roger Quadros wrote:
> On 06/04/16 09:09, Felipe Balbi wrote:
> >
> > Hi,
> >
> > Roger Quadros <[email protected]> writes:
> >> diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
> >> index 2ca2cef..6b1930d 100644
> >> --- a/drivers/usb/core/hcd.c
> >> +++ b/drivers/usb/core/hcd.c
> >> @@ -2706,6 +2706,7 @@ int usb_add_hcd(struct usb_hcd *hcd,
> >> int retval;
> >> struct usb_device *rhdev;
> >>
> >> + hcd->flags = 0;
> >

I am not sure if this usb_add(remove)_hcd pair is safe and clean enough
for start/stop host role. From my point, we may need to do like
.probe/.remove host platform driver interface. In that case, we can make
sure the clocks and regulators are off, and hcd will be zero-initialized
next time. Assume we are at gadget mode, we may not hope the vbus regulator
is still on which is for host only. So, this part may need to implement
by each user.

--

Best Regards,
Peter Chen

2016-04-07 10:40:56

by Roger Quadros

[permalink] [raw]
Subject: Re: [PATCH v6 01/12] usb: hcd: Initialize hcd->flags to 0

On 07/04/16 12:42, Peter Chen wrote:
> On Wed, Apr 06, 2016 at 09:32:22AM +0300, Roger Quadros wrote:
>> On 06/04/16 09:09, Felipe Balbi wrote:
>>>
>>> Hi,
>>>
>>> Roger Quadros <[email protected]> writes:
>>>> diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
>>>> index 2ca2cef..6b1930d 100644
>>>> --- a/drivers/usb/core/hcd.c
>>>> +++ b/drivers/usb/core/hcd.c
>>>> @@ -2706,6 +2706,7 @@ int usb_add_hcd(struct usb_hcd *hcd,
>>>> int retval;
>>>> struct usb_device *rhdev;
>>>>
>>>> + hcd->flags = 0;
>>>
>
> I am not sure if this usb_add(remove)_hcd pair is safe and clean enough
> for start/stop host role. From my point, we may need to do like
> .probe/.remove host platform driver interface. In that case, we can make

probe and remove are meant to be called from bus layer.
I do not see a way how OTG framework can call probe/remove of HCD driver.
Some HCDs may be platform devices, some PCI, so different entities are calling
the HCD .probe hook.

> sure the clocks and regulators are off, and hcd will be zero-initialized

why can't we make that sure that is taken care of within the hcd_ops?
Why should some driver keep its regulators and clocks enabled when hcd is stopped?
It doesn't need to. If it is doing so now, it needs to be fixed.

> next time. Assume we are at gadget mode, we may not hope the vbus regulator
> is still on which is for host only. So, this part may need to implement
> by each user.
>

Yes, correctness of this has to be taken care by each driver.

cheers,
-roger

2016-04-07 11:45:44

by Roger Quadros

[permalink] [raw]
Subject: Re: [PATCH v6 07/12] usb: otg: add OTG/dual-role core

Hi,

On 07/04/16 11:52, Yoshihiro Shimoda wrote:
> Hi,
>
>> From: Roger Quadros
>> Sent: Tuesday, April 05, 2016 11:05 PM
>>
>> 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 <[email protected]>
>> ---
>
> I tried to implement this framework on my environment,
> and it seemed work if I added a local hack to this patch :)

Great. Thanks for testing :).

>
> < snip >
>> + /* 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");
>
> My environment is arm64 / r8a7795, renesas_usbhs (as a gadget), EHCI and OHCI.
> In this case, OHCI driver is also a primary_hcd. So, this error happened.
> To avoid this, I assumes that the OHCI hcd is as shared_hcd like a patch below:
>
> diff --git a/drivers/usb/common/usb-otg.c b/drivers/usb/common/usb-otg.c
> index 41e762a..4d7f043 100644
> --- a/drivers/usb/common/usb-otg.c
> +++ b/drivers/usb/common/usb-otg.c
> @@ -825,11 +825,16 @@ int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
> if (otg->primary_hcd.hcd) {
> /* probably a shared HCD ? */
> if (usb_otg_hcd_is_primary_hcd(hcd)) {
> + if (hcd->driver->flags & HCD_USB11) {
> + dev_info(otg_dev, "this assumes usb 1.1 hc is as shared_hcd\n");
> + goto check_shared_hcd;
> + }
> dev_err(otg_dev, "otg: primary host already registered\n");
> goto err;
> }
>
> if (hcd->shared_hcd == otg->primary_hcd.hcd) {
> +check_shared_hcd:
> if (otg->shared_hcd.hcd) {
> dev_err(otg_dev, "otg: shared host already registered\n");
> goto err;
>
> What do you think this local hack?

Is it guaranteed that EHCI hcd registers before OHCI hcd?
If not we need to improve the code still.
We will also need to remove the constraint that primary_hcd must be registered
first in usb_otg_register_hcd(). I think that constraint is no longer
needed anyways.

If EHCI & OHCI HCDs register before OTG driver then things will break unless
we fix usb_otg_hcd_wait_add(). We need to add this HCD_USB11 check there to
populate wait->shared_hcd.


> I also wonder if array of hcd may be good for both xHCI and EHCI/OHCI.
> For example of xHCI:
> - otg->hcds[0] = primary_hcd
> - otg->hcds[1] = shared_hcd
>
> For example of EHCI/OHCI:
> - otg->hcds[0] = primary_hcd of EHCI
> - otg->hcds[1] = primary_hcd of OHCI

The bigger problem is that how do we know in the OHCI/EHCI case that we need to wait
for both of them before starting the OTG FSM?
Some systems might use just OHCI or just EHCI.

There is no guarantee that OTG driver registers before the HCDs so this piece
of information must come from the HCD itself. i.e. whether it needs a companion or not.

cheers,
-roger

2016-04-08 01:08:22

by Peter Chen

[permalink] [raw]
Subject: Re: [PATCH v6 01/12] usb: hcd: Initialize hcd->flags to 0

On Thu, Apr 07, 2016 at 01:40:21PM +0300, Roger Quadros wrote:
> On 07/04/16 12:42, Peter Chen wrote:
> > On Wed, Apr 06, 2016 at 09:32:22AM +0300, Roger Quadros wrote:
> >> On 06/04/16 09:09, Felipe Balbi wrote:
> >>>
> >>> Hi,
> >>>
> >>> Roger Quadros <[email protected]> writes:
> >>>> diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
> >>>> index 2ca2cef..6b1930d 100644
> >>>> --- a/drivers/usb/core/hcd.c
> >>>> +++ b/drivers/usb/core/hcd.c
> >>>> @@ -2706,6 +2706,7 @@ int usb_add_hcd(struct usb_hcd *hcd,
> >>>> int retval;
> >>>> struct usb_device *rhdev;
> >>>>
> >>>> + hcd->flags = 0;
> >>>
> >
> > I am not sure if this usb_add(remove)_hcd pair is safe and clean enough
> > for start/stop host role. From my point, we may need to do like
> > .probe/.remove host platform driver interface. In that case, we can make
>
> probe and remove are meant to be called from bus layer.
> I do not see a way how OTG framework can call probe/remove of HCD driver.
> Some HCDs may be platform devices, some PCI, so different entities are calling
> the HCD .probe hook.
>
> > sure the clocks and regulators are off, and hcd will be zero-initialized
>
> why can't we make that sure that is taken care of within the hcd_ops?
> Why should some driver keep its regulators and clocks enabled when hcd is stopped?
> It doesn't need to. If it is doing so now, it needs to be fixed.
>

Well, you may misunderstand me. I mean your hcd_ops->start or ->stop
is hard to be a general one which only calls usb_hcd_add or
usb_hcd_remove. It needs to implement like .probe or .remove at platform
driver, some example code like host_start and host_stop at
drivers/usb/chipidea/host.c.

--

Best Regards,
Peter Chen

2016-04-08 07:16:49

by Roger Quadros

[permalink] [raw]
Subject: Re: [PATCH v6 01/12] usb: hcd: Initialize hcd->flags to 0

On 08/04/16 04:01, Peter Chen wrote:
> On Thu, Apr 07, 2016 at 01:40:21PM +0300, Roger Quadros wrote:
>> On 07/04/16 12:42, Peter Chen wrote:
>>> On Wed, Apr 06, 2016 at 09:32:22AM +0300, Roger Quadros wrote:
>>>> On 06/04/16 09:09, Felipe Balbi wrote:
>>>>>
>>>>> Hi,
>>>>>
>>>>> Roger Quadros <[email protected]> writes:
>>>>>> diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
>>>>>> index 2ca2cef..6b1930d 100644
>>>>>> --- a/drivers/usb/core/hcd.c
>>>>>> +++ b/drivers/usb/core/hcd.c
>>>>>> @@ -2706,6 +2706,7 @@ int usb_add_hcd(struct usb_hcd *hcd,
>>>>>> int retval;
>>>>>> struct usb_device *rhdev;
>>>>>>
>>>>>> + hcd->flags = 0;
>>>>>
>>>
>>> I am not sure if this usb_add(remove)_hcd pair is safe and clean enough
>>> for start/stop host role. From my point, we may need to do like
>>> .probe/.remove host platform driver interface. In that case, we can make
>>
>> probe and remove are meant to be called from bus layer.
>> I do not see a way how OTG framework can call probe/remove of HCD driver.
>> Some HCDs may be platform devices, some PCI, so different entities are calling
>> the HCD .probe hook.
>>
>>> sure the clocks and regulators are off, and hcd will be zero-initialized
>>
>> why can't we make that sure that is taken care of within the hcd_ops?
>> Why should some driver keep its regulators and clocks enabled when hcd is stopped?
>> It doesn't need to. If it is doing so now, it needs to be fixed.
>>
>
> Well, you may misunderstand me. I mean your hcd_ops->start or ->stop
> is hard to be a general one which only calls usb_hcd_add or
> usb_hcd_remove. It needs to implement like .probe or .remove at platform
> driver, some example code like host_start and host_stop at
> drivers/usb/chipidea/host.c.
>
The only extra thing the host_start/stop() of that driver is doing is
enabling/disabling the VBUS regulator.
In the OTG/dual role scope VBUS regulator handling has to be done via the
OTG driver using the otg ops otg_drv_vbus() and not at HCD level. Do you agree?

I don't want to complicate the OTG to HCD interface by adding new hooks there.
If HCD driver wants to do something special for OTG case it can always do that
within the struct hc_driver interface. But ideally OTG specific handling must
be done in the OTG driver. All that the HCD driver should care about is making sure
all used resources are disabled once usb_remove_hcd() is called.

cheers,
-roger

2016-04-08 07:52:33

by Peter Chen

[permalink] [raw]
Subject: Re: [PATCH v6 01/12] usb: hcd: Initialize hcd->flags to 0

On Fri, Apr 08, 2016 at 10:16:30AM +0300, Roger Quadros wrote:
> On 08/04/16 04:01, Peter Chen wrote:
> > On Thu, Apr 07, 2016 at 01:40:21PM +0300, Roger Quadros wrote:
> >> On 07/04/16 12:42, Peter Chen wrote:
> >>> On Wed, Apr 06, 2016 at 09:32:22AM +0300, Roger Quadros wrote:
> >>>> On 06/04/16 09:09, Felipe Balbi wrote:
> >>>>>
> >>>>> Hi,
> >>>>>
> >>>>> Roger Quadros <[email protected]> writes:
> >>>>>> diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
> >>>>>> index 2ca2cef..6b1930d 100644
> >>>>>> --- a/drivers/usb/core/hcd.c
> >>>>>> +++ b/drivers/usb/core/hcd.c
> >>>>>> @@ -2706,6 +2706,7 @@ int usb_add_hcd(struct usb_hcd *hcd,
> >>>>>> int retval;
> >>>>>> struct usb_device *rhdev;
> >>>>>>
> >>>>>> + hcd->flags = 0;
> >>>>>
> >>>
> >>> I am not sure if this usb_add(remove)_hcd pair is safe and clean enough
> >>> for start/stop host role. From my point, we may need to do like
> >>> .probe/.remove host platform driver interface. In that case, we can make
> >>
> >> probe and remove are meant to be called from bus layer.
> >> I do not see a way how OTG framework can call probe/remove of HCD driver.
> >> Some HCDs may be platform devices, some PCI, so different entities are calling
> >> the HCD .probe hook.
> >>
> >>> sure the clocks and regulators are off, and hcd will be zero-initialized
> >>
> >> why can't we make that sure that is taken care of within the hcd_ops?
> >> Why should some driver keep its regulators and clocks enabled when hcd is stopped?
> >> It doesn't need to. If it is doing so now, it needs to be fixed.
> >>
> >
> > Well, you may misunderstand me. I mean your hcd_ops->start or ->stop
> > is hard to be a general one which only calls usb_hcd_add or
> > usb_hcd_remove. It needs to implement like .probe or .remove at platform
> > driver, some example code like host_start and host_stop at
> > drivers/usb/chipidea/host.c.
> >
> The only extra thing the host_start/stop() of that driver is doing is
> enabling/disabling the VBUS regulator.
> In the OTG/dual role scope VBUS regulator handling has to be done via the
> OTG driver using the otg ops otg_drv_vbus() and not at HCD level. Do you agree?
>

I agree with you that vbus handling should be handled by otg_drv_vbus
for OTG case.

> I don't want to complicate the OTG to HCD interface by adding new hooks there.
> If HCD driver wants to do something special for OTG case it can always do that
> within the struct hc_driver interface. But ideally OTG specific handling must
> be done in the OTG driver. All that the HCD driver should care about is making sure
> all used resources are disabled once usb_remove_hcd() is called.
>

In this patch, we set hcd->flag as 0 to fix one problem, but if any
problems are still existed due to non-zero hcd structure. We have
tested a lot for insmod/rmmod procedure for host driver, it doesn't
show many problems. In a word, once the host role is stopped do we
need to remove all things belong to host, and re-initialized all things
belong to host when the host role is re-started? From my point is 'yes'.

host_stop()
{
usb_remove_hcd(hcd);
usb_put_hcd(hcd);
platform_deinit(); /* clock, gpio, etc */
}

host_start()
{
platform_init(); /* clock, gpio, etc */
usb_create_hcd();
... /* initialize hcd structure from platform information */
usb_add_hcd(hcd);
}

Maybe you have already done something, but there is no user now.
Things will be more clear after your post your dwc3 changes :)

--

Best Regards,
Peter Chen

2016-04-08 11:22:33

by Yoshihiro Shimoda

[permalink] [raw]
Subject: RE: [PATCH v6 07/12] usb: otg: add OTG/dual-role core

Hi,

> From: Roger Quadros
> Sent: Thursday, April 07, 2016 8:45 PM
>
> Hi,
>
> On 07/04/16 11:52, Yoshihiro Shimoda wrote:
> > Hi,
> >
> >> From: Roger Quadros
> >> Sent: Tuesday, April 05, 2016 11:05 PM
< snip >
> > diff --git a/drivers/usb/common/usb-otg.c b/drivers/usb/common/usb-otg.c
> > index 41e762a..4d7f043 100644
> > --- a/drivers/usb/common/usb-otg.c
> > +++ b/drivers/usb/common/usb-otg.c
> > @@ -825,11 +825,16 @@ int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
> > if (otg->primary_hcd.hcd) {
> > /* probably a shared HCD ? */
> > if (usb_otg_hcd_is_primary_hcd(hcd)) {
> > + if (hcd->driver->flags & HCD_USB11) {
> > + dev_info(otg_dev, "this assumes usb 1.1 hc is as shared_hcd\n");
> > + goto check_shared_hcd;
> > + }
> > dev_err(otg_dev, "otg: primary host already registered\n");
> > goto err;
> > }
> >
> > if (hcd->shared_hcd == otg->primary_hcd.hcd) {
> > +check_shared_hcd:
> > if (otg->shared_hcd.hcd) {
> > dev_err(otg_dev, "otg: shared host already registered\n");
> > goto err;
> >
> > What do you think this local hack?
>
> Is it guaranteed that EHCI hcd registers before OHCI hcd?

Thank you for the comment. No, it is not guaranteed.

> If not we need to improve the code still.
> We will also need to remove the constraint that primary_hcd must be registered
> first in usb_otg_register_hcd(). I think that constraint is no longer
> needed anyways.

I got it.
So, I made a patch to avoid the constraint using an additional property "otg-hcds".
The patch is in this end of email. What do you think about the patch?

> If EHCI & OHCI HCDs register before OTG driver then things will break unless
> we fix usb_otg_hcd_wait_add(). We need to add this HCD_USB11 check there to
> populate wait->shared_hcd.

I see. In my environment, since EHCI & OHCI HCDs need phy driver and phy driver
will calls usb_otg_register(), the order is right. In other words,
EHCI & OHCI HCDs never register before OTG driver because even if EHCI driver
is probed first, the driver will be deferred (EHCI driver needs the phy driver).

> > I also wonder if array of hcd may be good for both xHCI and EHCI/OHCI.
> > For example of xHCI:
> > - otg->hcds[0] = primary_hcd
> > - otg->hcds[1] = shared_hcd
> >
> > For example of EHCI/OHCI:
> > - otg->hcds[0] = primary_hcd of EHCI
> > - otg->hcds[1] = primary_hcd of OHCI
>
> The bigger problem is that how do we know in the OHCI/EHCI case that we need to wait
> for both of them before starting the OTG FSM?
> Some systems might use just OHCI or just EHCI.
>
> There is no guarantee that OTG driver registers before the HCDs so this piece
> of information must come from the HCD itself. i.e. whether it needs a companion or not.

I understood these problems. I wonder if my patch can resolve these problems.

Best regards,
Yoshihiro Shimoda
---
diff --git a/Documentation/devicetree/bindings/usb/generic.txt b/Documentation/devicetree/bindings/usb/generic.txt
index f6866c1..518b9eb 100644
--- a/Documentation/devicetree/bindings/usb/generic.txt
+++ b/Documentation/devicetree/bindings/usb/generic.txt
@@ -27,6 +27,12 @@ Optional properties:
- otg-controller: phandle to otg controller. Host or gadget controllers can
contain this property to link it to a particular OTG
controller.
+ - otg-hcds: phandle to host controller(s). An OTG controller can contain this
+ property. The first one is as "primary", and (optional) the second
+ one is as "shared".
+ - For example for xHCI: otg-hcds = <&xhci>, <&xhci>;
+ - For example for EHCI/OHCI: otg-hcds = <&ehci>, <&ohci>;
+ - For example for just EHCI: otg-hcds = <&ehci>;

This is an attribute to a USB controller such as:

diff --git a/arch/arm64/boot/dts/renesas/r8a7795.dtsi b/arch/arm64/boot/dts/renesas/r8a7795.dtsi
index 741d9d2..9dcf76ac 100644
--- a/arch/arm64/boot/dts/renesas/r8a7795.dtsi
+++ b/arch/arm64/boot/dts/renesas/r8a7795.dtsi
@@ -1253,6 +1253,7 @@
compatible = "renesas,usb2-phy-r8a7795";
reg = <0 0xee080200 0 0x700>;
interrupts = <GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH>;
+ otg-hcds = <&ehci0>, <&ohci0>;
clocks = <&cpg CPG_MOD 703>;
power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
#phy-cells = <0>;
diff --git a/drivers/usb/common/usb-otg.c b/drivers/usb/common/usb-otg.c
index 41e762a..9a78482 100644
--- a/drivers/usb/common/usb-otg.c
+++ b/drivers/usb/common/usb-otg.c
@@ -258,7 +258,8 @@ static void usb_otg_flush_wait(struct device *otg_dev)

dev_dbg(otg_dev, "otg: registering pending host/gadget\n");
gadget = &wait->gcd;
- if (gadget)
+ /* I'm not sure but gadget->gadget is possible to be NULL */
+ if (gadget && gadget->gadget)
usb_otg_register_gadget(gadget->gadget, gadget->ops);

host = &wait->primary_hcd;
@@ -567,6 +568,8 @@ struct usb_otg *usb_otg_register(struct device *dev,
{
struct usb_otg *otg;
struct otg_wait_data *wait;
+ struct device_node *np;
+ struct platform_device *pdev;
int ret = 0;

if (!dev || !config || !config->fsm_ops)
@@ -616,6 +619,40 @@ struct usb_otg *usb_otg_register(struct device *dev,
list_add_tail(&otg->list, &otg_list);
mutex_unlock(&otg_list_mutex);

+ /* Get primary hcd device (mandatory) */
+ np = of_parse_phandle(dev->of_node, "otg-hcds", 0);
+ if (!np) {
+ dev_err(dev, "no otg-hcds\n");
+ ret = -EINVAL;
+ goto err_dt;
+ }
+ pdev = of_find_device_by_node(np);
+ of_node_put(np);
+ if (!pdev) {
+ dev_err(dev, "coulnd't get primary hcd device\n");
+ ret = -ENODEV;
+ goto err_dt;
+ }
+ otg->primary_dev = &pdev->dev;
+
+ /* Get shared hcd device (optional) */
+ np = of_parse_phandle(dev->of_node, "otg-hcds", 1);
+ if (np) {
+ pdev = of_find_device_by_node(np);
+ of_node_put(np);
+ if (!pdev) {
+ dev_err(dev, "coulnd't get shared hcd device\n");
+ ret = -ENODEV;
+ goto err_dt;
+ }
+ otg->shared_dev = &pdev->dev;
+ } else {
+ dev_dbg(dev, "no shared hcd\n");
+ }
+
+ if (otg->primary_dev != otg->shared_dev)
+ otg->flags |= OTG_FLAG_SHARED_IS_2ND_PRIMARY;
+
/* were we in wait list? */
mutex_lock(&wait_list_mutex);
wait = usb_otg_get_wait(dev);
@@ -627,6 +664,8 @@ struct usb_otg *usb_otg_register(struct device *dev,

return otg;

+err_dt:
+ destroy_workqueue(otg->wq);
err_wq:
kfree(otg);
unlock:
@@ -822,50 +861,42 @@ 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)) {
- dev_err(otg_dev, "otg: primary host already registered\n");
- goto err;
+ if (!otg->primary_hcd.hcd) {
+ dev_info(hcd_dev, "%s: primary %s, now %s\n", __func__,
+ dev_name(otg->primary_dev),
+ dev_name(hcd->self.controller));
+ if (otg->primary_dev == hcd->self.controller) {
+ 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));
}
+ }

- 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;
- }
-
+ if (!otg->shared_hcd.hcd && (!usb_otg_hcd_is_primary_hcd(hcd) ||
+ otg->flags & OTG_FLAG_SHARED_IS_2ND_PRIMARY)) {
+ dev_info(hcd_dev, "%s: shared %s, now %s\n", __func__,
+ dev_name(otg->shared_dev),
+ dev_name(hcd->self.controller));
+ if (otg->shared_dev == hcd->self.controller) {
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) {
+ if (otg->primary_hcd.hcd && (!otg->shared_dev ||
+ (otg->shared_dev && otg->shared_hcd.hcd))) {
otg->host = hcd_to_bus(hcd);
/* FIXME: set bus->otg_port if this is true OTG port with HNP */

@@ -878,10 +909,6 @@ int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
mutex_unlock(&otg->fsm.lock);

return 0;
-
-err:
- mutex_unlock(&otg->fsm.lock);
- return -EINVAL;
}
EXPORT_SYMBOL_GPL(usb_otg_register_hcd);

diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h
index b094352..ed08865 100644
--- a/include/linux/usb/otg.h
+++ b/include/linux/usb/otg.h
@@ -51,6 +51,8 @@ struct otg_hcd {
* @fsm: otg finite state machine
* @hcd_ops: host controller interface
* ------- internal use only -------
+ * @primary_dev: primary host device for matching
+ * @shared_dev: (optional) shared or other primary host device for matching
* @primary_hcd: primary host state and interface
* @shared_hcd: shared host state and interface
* @gadget_ops: gadget controller interface
@@ -75,6 +77,8 @@ struct usb_otg {
struct otg_hcd_ops *hcd_ops;

/* internal use only */
+ struct device *primary_dev;
+ struct device *shared_dev;
struct otg_hcd primary_hcd;
struct otg_hcd shared_hcd;
struct otg_gadget_ops *gadget_ops;
@@ -84,6 +88,7 @@ struct usb_otg {
u32 flags;
#define OTG_FLAG_GADGET_RUNNING (1 << 0)
#define OTG_FLAG_HOST_RUNNING (1 << 1)
+#define OTG_FLAG_SHARED_IS_2ND_PRIMARY (1 << 2)
/* use otg->fsm.lock for serializing access */

/*------------- deprecated interface -----------------------------*/


2016-04-11 10:55:01

by Roger Quadros

[permalink] [raw]
Subject: Re: [PATCH v6 07/12] usb: otg: add OTG/dual-role core

On 08/04/16 14:22, Yoshihiro Shimoda wrote:
> Hi,
>
>> From: Roger Quadros
>> Sent: Thursday, April 07, 2016 8:45 PM
>>
>> Hi,
>>
>> On 07/04/16 11:52, Yoshihiro Shimoda wrote:
>>> Hi,
>>>
>>>> From: Roger Quadros
>>>> Sent: Tuesday, April 05, 2016 11:05 PM
> < snip >
>>> diff --git a/drivers/usb/common/usb-otg.c b/drivers/usb/common/usb-otg.c
>>> index 41e762a..4d7f043 100644
>>> --- a/drivers/usb/common/usb-otg.c
>>> +++ b/drivers/usb/common/usb-otg.c
>>> @@ -825,11 +825,16 @@ int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
>>> if (otg->primary_hcd.hcd) {
>>> /* probably a shared HCD ? */
>>> if (usb_otg_hcd_is_primary_hcd(hcd)) {
>>> + if (hcd->driver->flags & HCD_USB11) {
>>> + dev_info(otg_dev, "this assumes usb 1.1 hc is as shared_hcd\n");
>>> + goto check_shared_hcd;
>>> + }
>>> dev_err(otg_dev, "otg: primary host already registered\n");
>>> goto err;
>>> }
>>>
>>> if (hcd->shared_hcd == otg->primary_hcd.hcd) {
>>> +check_shared_hcd:
>>> if (otg->shared_hcd.hcd) {
>>> dev_err(otg_dev, "otg: shared host already registered\n");
>>> goto err;
>>>
>>> What do you think this local hack?
>>
>> Is it guaranteed that EHCI hcd registers before OHCI hcd?
>
> Thank you for the comment. No, it is not guaranteed.
>
>> If not we need to improve the code still.
>> We will also need to remove the constraint that primary_hcd must be registered
>> first in usb_otg_register_hcd(). I think that constraint is no longer
>> needed anyways.
>
> I got it.
> So, I made a patch to avoid the constraint using an additional property "otg-hcds".
> The patch is in this end of email. What do you think about the patch?

This might only solve the issue for device tree but what about non-device tree?
We need to deal with both cases.

>
>> If EHCI & OHCI HCDs register before OTG driver then things will break unless
>> we fix usb_otg_hcd_wait_add(). We need to add this HCD_USB11 check there to
>> populate wait->shared_hcd.
>
> I see. In my environment, since EHCI & OHCI HCDs need phy driver and phy driver
> will calls usb_otg_register(), the order is right. In other words,
> EHCI & OHCI HCDs never register before OTG driver because even if EHCI driver
> is probed first, the driver will be deferred (EHCI driver needs the phy driver).

But still we cannot assume this is true for all platforms. OTG driver can also
be a separate entity than PHY driver.

>
>>> I also wonder if array of hcd may be good for both xHCI and EHCI/OHCI.
>>> For example of xHCI:
>>> - otg->hcds[0] = primary_hcd
>>> - otg->hcds[1] = shared_hcd
>>>
>>> For example of EHCI/OHCI:
>>> - otg->hcds[0] = primary_hcd of EHCI
>>> - otg->hcds[1] = primary_hcd of OHCI
>>
>> The bigger problem is that how do we know in the OHCI/EHCI case that we need to wait
>> for both of them before starting the OTG FSM?
>> Some systems might use just OHCI or just EHCI.
>>
>> There is no guarantee that OTG driver registers before the HCDs so this piece
>> of information must come from the HCD itself. i.e. whether it needs a companion or not.
>
> I understood these problems. I wonder if my patch can resolve these problems.
>
> Best regards,
> Yoshihiro Shimoda
> ---
> diff --git a/Documentation/devicetree/bindings/usb/generic.txt b/Documentation/devicetree/bindings/usb/generic.txt
> index f6866c1..518b9eb 100644
> --- a/Documentation/devicetree/bindings/usb/generic.txt
> +++ b/Documentation/devicetree/bindings/usb/generic.txt
> @@ -27,6 +27,12 @@ Optional properties:
> - otg-controller: phandle to otg controller. Host or gadget controllers can
> contain this property to link it to a particular OTG
> controller.
> + - otg-hcds: phandle to host controller(s). An OTG controller can contain this
> + property. The first one is as "primary", and (optional) the second
> + one is as "shared".
> + - For example for xHCI: otg-hcds = <&xhci>, <&xhci>;
> + - For example for EHCI/OHCI: otg-hcds = <&ehci>, <&ohci>;
> + - For example for just EHCI: otg-hcds = <&ehci>;
>

This is kind of duplicating the information. We are already specifying in the
hcd nodes as to which otg controller it is linked to.

Instead, how about just adding a boolean property in the otg node saying that
HCD needs companion. e.g.
hcd-needs-compainion: must be present if otg controller is dealing with
EHCI host controller that needs a companion OHCI host controller.

This can also be added in struct usb_otg_config so that it can deal with non-DT cases.

So if this property is true, the OTG core will wait for 2 primary HCDs to be registered.

cheers,
-roger

> This is an attribute to a USB controller such as:
>
> diff --git a/arch/arm64/boot/dts/renesas/r8a7795.dtsi b/arch/arm64/boot/dts/renesas/r8a7795.dtsi
> index 741d9d2..9dcf76ac 100644
> --- a/arch/arm64/boot/dts/renesas/r8a7795.dtsi
> +++ b/arch/arm64/boot/dts/renesas/r8a7795.dtsi
> @@ -1253,6 +1253,7 @@
> compatible = "renesas,usb2-phy-r8a7795";
> reg = <0 0xee080200 0 0x700>;
> interrupts = <GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH>;
> + otg-hcds = <&ehci0>, <&ohci0>;
> clocks = <&cpg CPG_MOD 703>;
> power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
> #phy-cells = <0>;
> diff --git a/drivers/usb/common/usb-otg.c b/drivers/usb/common/usb-otg.c
> index 41e762a..9a78482 100644
> --- a/drivers/usb/common/usb-otg.c
> +++ b/drivers/usb/common/usb-otg.c
> @@ -258,7 +258,8 @@ static void usb_otg_flush_wait(struct device *otg_dev)
>
> dev_dbg(otg_dev, "otg: registering pending host/gadget\n");
> gadget = &wait->gcd;
> - if (gadget)
> + /* I'm not sure but gadget->gadget is possible to be NULL */
> + if (gadget && gadget->gadget)
> usb_otg_register_gadget(gadget->gadget, gadget->ops);
>
> host = &wait->primary_hcd;
> @@ -567,6 +568,8 @@ struct usb_otg *usb_otg_register(struct device *dev,
> {
> struct usb_otg *otg;
> struct otg_wait_data *wait;
> + struct device_node *np;
> + struct platform_device *pdev;
> int ret = 0;
>
> if (!dev || !config || !config->fsm_ops)
> @@ -616,6 +619,40 @@ struct usb_otg *usb_otg_register(struct device *dev,
> list_add_tail(&otg->list, &otg_list);
> mutex_unlock(&otg_list_mutex);
>
> + /* Get primary hcd device (mandatory) */
> + np = of_parse_phandle(dev->of_node, "otg-hcds", 0);
> + if (!np) {
> + dev_err(dev, "no otg-hcds\n");
> + ret = -EINVAL;
> + goto err_dt;
> + }
> + pdev = of_find_device_by_node(np);
> + of_node_put(np);
> + if (!pdev) {
> + dev_err(dev, "coulnd't get primary hcd device\n");
> + ret = -ENODEV;
> + goto err_dt;
> + }
> + otg->primary_dev = &pdev->dev;
> +
> + /* Get shared hcd device (optional) */
> + np = of_parse_phandle(dev->of_node, "otg-hcds", 1);
> + if (np) {
> + pdev = of_find_device_by_node(np);
> + of_node_put(np);
> + if (!pdev) {
> + dev_err(dev, "coulnd't get shared hcd device\n");
> + ret = -ENODEV;
> + goto err_dt;
> + }
> + otg->shared_dev = &pdev->dev;
> + } else {
> + dev_dbg(dev, "no shared hcd\n");
> + }
> +
> + if (otg->primary_dev != otg->shared_dev)
> + otg->flags |= OTG_FLAG_SHARED_IS_2ND_PRIMARY;
> +
> /* were we in wait list? */
> mutex_lock(&wait_list_mutex);
> wait = usb_otg_get_wait(dev);
> @@ -627,6 +664,8 @@ struct usb_otg *usb_otg_register(struct device *dev,
>
> return otg;
>
> +err_dt:
> + destroy_workqueue(otg->wq);
> err_wq:
> kfree(otg);
> unlock:
> @@ -822,50 +861,42 @@ 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)) {
> - dev_err(otg_dev, "otg: primary host already registered\n");
> - goto err;
> + if (!otg->primary_hcd.hcd) {
> + dev_info(hcd_dev, "%s: primary %s, now %s\n", __func__,
> + dev_name(otg->primary_dev),
> + dev_name(hcd->self.controller));
> + if (otg->primary_dev == hcd->self.controller) {
> + 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));
> }
> + }
>
> - 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;
> - }
> -
> + if (!otg->shared_hcd.hcd && (!usb_otg_hcd_is_primary_hcd(hcd) ||
> + otg->flags & OTG_FLAG_SHARED_IS_2ND_PRIMARY)) {
> + dev_info(hcd_dev, "%s: shared %s, now %s\n", __func__,
> + dev_name(otg->shared_dev),
> + dev_name(hcd->self.controller));
> + if (otg->shared_dev == hcd->self.controller) {
> 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) {
> + if (otg->primary_hcd.hcd && (!otg->shared_dev ||
> + (otg->shared_dev && otg->shared_hcd.hcd))) {
> otg->host = hcd_to_bus(hcd);
> /* FIXME: set bus->otg_port if this is true OTG port with HNP */
>
> @@ -878,10 +909,6 @@ int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
> mutex_unlock(&otg->fsm.lock);
>
> return 0;
> -
> -err:
> - mutex_unlock(&otg->fsm.lock);
> - return -EINVAL;
> }
> EXPORT_SYMBOL_GPL(usb_otg_register_hcd);
>
> diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h
> index b094352..ed08865 100644
> --- a/include/linux/usb/otg.h
> +++ b/include/linux/usb/otg.h
> @@ -51,6 +51,8 @@ struct otg_hcd {
> * @fsm: otg finite state machine
> * @hcd_ops: host controller interface
> * ------- internal use only -------
> + * @primary_dev: primary host device for matching
> + * @shared_dev: (optional) shared or other primary host device for matching
> * @primary_hcd: primary host state and interface
> * @shared_hcd: shared host state and interface
> * @gadget_ops: gadget controller interface
> @@ -75,6 +77,8 @@ struct usb_otg {
> struct otg_hcd_ops *hcd_ops;
>
> /* internal use only */
> + struct device *primary_dev;
> + struct device *shared_dev;
> struct otg_hcd primary_hcd;
> struct otg_hcd shared_hcd;
> struct otg_gadget_ops *gadget_ops;
> @@ -84,6 +88,7 @@ struct usb_otg {
> u32 flags;
> #define OTG_FLAG_GADGET_RUNNING (1 << 0)
> #define OTG_FLAG_HOST_RUNNING (1 << 1)
> +#define OTG_FLAG_SHARED_IS_2ND_PRIMARY (1 << 2)
> /* use otg->fsm.lock for serializing access */
>
> /*------------- deprecated interface -----------------------------*/
>
>

2016-04-14 08:37:07

by Yoshihiro Shimoda

[permalink] [raw]
Subject: RE: [PATCH v6 07/12] usb: otg: add OTG/dual-role core

Hi,

> From: Roger Quadros
> Sent: Monday, April 11, 2016 7:55 PM
>
> On 08/04/16 14:22, Yoshihiro Shimoda wrote:
> > Hi,
> >
> >> From: Roger Quadros
> >> Sent: Thursday, April 07, 2016 8:45 PM
> >>
> >> Hi,
> >>
> >> On 07/04/16 11:52, Yoshihiro Shimoda wrote:
> >>> Hi,
> >>>
> >>>> From: Roger Quadros
> >>>> Sent: Tuesday, April 05, 2016 11:05 PM
> > < snip >
> >>> diff --git a/drivers/usb/common/usb-otg.c b/drivers/usb/common/usb-otg.c
> >>> index 41e762a..4d7f043 100644
> >>> --- a/drivers/usb/common/usb-otg.c
> >>> +++ b/drivers/usb/common/usb-otg.c
> >>> @@ -825,11 +825,16 @@ int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
> >>> if (otg->primary_hcd.hcd) {
> >>> /* probably a shared HCD ? */
> >>> if (usb_otg_hcd_is_primary_hcd(hcd)) {
> >>> + if (hcd->driver->flags & HCD_USB11) {
> >>> + dev_info(otg_dev, "this assumes usb 1.1 hc is as shared_hcd\n");
> >>> + goto check_shared_hcd;
> >>> + }
> >>> dev_err(otg_dev, "otg: primary host already registered\n");
> >>> goto err;
> >>> }
> >>>
> >>> if (hcd->shared_hcd == otg->primary_hcd.hcd) {
> >>> +check_shared_hcd:
> >>> if (otg->shared_hcd.hcd) {
> >>> dev_err(otg_dev, "otg: shared host already registered\n");
> >>> goto err;
> >>>
> >>> What do you think this local hack?
> >>
> >> Is it guaranteed that EHCI hcd registers before OHCI hcd?
> >
> > Thank you for the comment. No, it is not guaranteed.
> >
> >> If not we need to improve the code still.
> >> We will also need to remove the constraint that primary_hcd must be registered
> >> first in usb_otg_register_hcd(). I think that constraint is no longer
> >> needed anyways.
> >
> > I got it.
> > So, I made a patch to avoid the constraint using an additional property "otg-hcds".
> > The patch is in this end of email. What do you think about the patch?
>
> This might only solve the issue for device tree but what about non-device tree?
> We need to deal with both cases.

Thank you for the comment. I got it.

> >> If EHCI & OHCI HCDs register before OTG driver then things will break unless
> >> we fix usb_otg_hcd_wait_add(). We need to add this HCD_USB11 check there to
> >> populate wait->shared_hcd.
> >
> > I see. In my environment, since EHCI & OHCI HCDs need phy driver and phy driver
> > will calls usb_otg_register(), the order is right. In other words,
> > EHCI & OHCI HCDs never register before OTG driver because even if EHCI driver
> > is probed first, the driver will be deferred (EHCI driver needs the phy driver).
>
> But still we cannot assume this is true for all platforms. OTG driver can also
> be a separate entity than PHY driver.

I understood it.

> >>> I also wonder if array of hcd may be good for both xHCI and EHCI/OHCI.
> >>> For example of xHCI:
> >>> - otg->hcds[0] = primary_hcd
> >>> - otg->hcds[1] = shared_hcd
> >>>
> >>> For example of EHCI/OHCI:
> >>> - otg->hcds[0] = primary_hcd of EHCI
> >>> - otg->hcds[1] = primary_hcd of OHCI
> >>
> >> The bigger problem is that how do we know in the OHCI/EHCI case that we need to wait
> >> for both of them before starting the OTG FSM?
> >> Some systems might use just OHCI or just EHCI.
> >>
> >> There is no guarantee that OTG driver registers before the HCDs so this piece
> >> of information must come from the HCD itself. i.e. whether it needs a companion or not.
> >
> > I understood these problems. I wonder if my patch can resolve these problems.
> >
> > Best regards,
> > Yoshihiro Shimoda
> > ---
> > diff --git a/Documentation/devicetree/bindings/usb/generic.txt b/Documentation/devicetree/bindings/usb/generic.txt
> > index f6866c1..518b9eb 100644
> > --- a/Documentation/devicetree/bindings/usb/generic.txt
> > +++ b/Documentation/devicetree/bindings/usb/generic.txt
> > @@ -27,6 +27,12 @@ Optional properties:
> > - otg-controller: phandle to otg controller. Host or gadget controllers can
> > contain this property to link it to a particular OTG
> > controller.
> > + - otg-hcds: phandle to host controller(s). An OTG controller can contain this
> > + property. The first one is as "primary", and (optional) the second
> > + one is as "shared".
> > + - For example for xHCI: otg-hcds = <&xhci>, <&xhci>;
> > + - For example for EHCI/OHCI: otg-hcds = <&ehci>, <&ohci>;
> > + - For example for just EHCI: otg-hcds = <&ehci>;
> >
>
> This is kind of duplicating the information. We are already specifying in the
> hcd nodes as to which otg controller it is linked to.
>
> Instead, how about just adding a boolean property in the otg node saying that
> HCD needs companion. e.g.
> hcd-needs-compainion: must be present if otg controller is dealing with
> EHCI host controller that needs a companion OHCI host controller.
>
> This can also be added in struct usb_otg_config so that it can deal with non-DT cases.
>
> So if this property is true, the OTG core will wait for 2 primary HCDs to be registered.

Thank you for your suggestion. I agree with you.
So, I made such a patch which. It is simple than my previous patch.
What do you think?

Best regards,
Yoshihiro Shimoda

---
Documentation/devicetree/bindings/usb/generic.txt | 3 +++
drivers/usb/common/common.c | 2 ++
drivers/usb/common/usb-otg.c | 11 +++++++----
include/linux/usb/otg.h | 2 ++
4 files changed, 14 insertions(+), 4 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/common.c b/drivers/usb/common/common.c
index e3d0161..8b74715 100644
--- a/drivers/usb/common/common.c
+++ b/drivers/usb/common/common.c
@@ -233,6 +233,8 @@ int of_usb_update_otg_caps(struct device_node *np,
if (of_find_property(np, "adp-disable", NULL) ||
(otg_caps->otg_rev < 0x0200))
otg_caps->adp_support = false;
+ if (of_find_property(np, "hcd-needs-companion", NULL))
+ otg_caps->needs_companion = true;

return 0;
}
diff --git a/drivers/usb/common/usb-otg.c b/drivers/usb/common/usb-otg.c
index 41e762a..e0df839 100644
--- a/drivers/usb/common/usb-otg.c
+++ b/drivers/usb/common/usb-otg.c
@@ -823,13 +823,15 @@ 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->caps->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->caps->needs_companion ||
+ (hcd->shared_hcd == otg->primary_hcd.hcd)) {
if (otg->shared_hcd.hcd) {
dev_err(otg_dev, "otg: shared host already registered\n");
goto err;
@@ -865,7 +867,8 @@ 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->caps->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 */

diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h
index b094352..64a7db8 100644
--- a/include/linux/usb/otg.h
+++ b/include/linux/usb/otg.h
@@ -112,12 +112,14 @@ struct usb_otg {
* @hnp_support: Indicates if the device supports HNP.
* @srp_support: Indicates if the device supports SRP.
* @adp_support: Indicates if the device supports ADP.
+ * @needs_companion: Indicates if the device needs a companion controller.
*/
struct usb_otg_caps {
u16 otg_rev;
bool hnp_support;
bool srp_support;
bool adp_support;
+ bool needs_companion;
};

/**
--
1.9.1

2016-04-14 11:00:17

by Roger Quadros

[permalink] [raw]
Subject: Re: [PATCH v6 07/12] usb: otg: add OTG/dual-role core

On 14/04/16 11:36, Yoshihiro Shimoda wrote:
> Hi,
>
>> From: Roger Quadros
>> Sent: Monday, April 11, 2016 7:55 PM
>>
>> On 08/04/16 14:22, Yoshihiro Shimoda wrote:
>>> Hi,
>>>
>>>> From: Roger Quadros
>>>> Sent: Thursday, April 07, 2016 8:45 PM
>>>>
>>>> Hi,
>>>>
>>>> On 07/04/16 11:52, Yoshihiro Shimoda wrote:
>>>>> Hi,
>>>>>
>>>>>> From: Roger Quadros
>>>>>> Sent: Tuesday, April 05, 2016 11:05 PM
>>> < snip >
>>>>> diff --git a/drivers/usb/common/usb-otg.c b/drivers/usb/common/usb-otg.c
>>>>> index 41e762a..4d7f043 100644
>>>>> --- a/drivers/usb/common/usb-otg.c
>>>>> +++ b/drivers/usb/common/usb-otg.c
>>>>> @@ -825,11 +825,16 @@ int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
>>>>> if (otg->primary_hcd.hcd) {
>>>>> /* probably a shared HCD ? */
>>>>> if (usb_otg_hcd_is_primary_hcd(hcd)) {
>>>>> + if (hcd->driver->flags & HCD_USB11) {
>>>>> + dev_info(otg_dev, "this assumes usb 1.1 hc is as shared_hcd\n");
>>>>> + goto check_shared_hcd;
>>>>> + }
>>>>> dev_err(otg_dev, "otg: primary host already registered\n");
>>>>> goto err;
>>>>> }
>>>>>
>>>>> if (hcd->shared_hcd == otg->primary_hcd.hcd) {
>>>>> +check_shared_hcd:
>>>>> if (otg->shared_hcd.hcd) {
>>>>> dev_err(otg_dev, "otg: shared host already registered\n");
>>>>> goto err;
>>>>>
>>>>> What do you think this local hack?
>>>>
>>>> Is it guaranteed that EHCI hcd registers before OHCI hcd?
>>>
>>> Thank you for the comment. No, it is not guaranteed.
>>>
>>>> If not we need to improve the code still.
>>>> We will also need to remove the constraint that primary_hcd must be registered
>>>> first in usb_otg_register_hcd(). I think that constraint is no longer
>>>> needed anyways.
>>>
>>> I got it.
>>> So, I made a patch to avoid the constraint using an additional property "otg-hcds".
>>> The patch is in this end of email. What do you think about the patch?
>>
>> This might only solve the issue for device tree but what about non-device tree?
>> We need to deal with both cases.
>
> Thank you for the comment. I got it.
>
>>>> If EHCI & OHCI HCDs register before OTG driver then things will break unless
>>>> we fix usb_otg_hcd_wait_add(). We need to add this HCD_USB11 check there to
>>>> populate wait->shared_hcd.
>>>
>>> I see. In my environment, since EHCI & OHCI HCDs need phy driver and phy driver
>>> will calls usb_otg_register(), the order is right. In other words,
>>> EHCI & OHCI HCDs never register before OTG driver because even if EHCI driver
>>> is probed first, the driver will be deferred (EHCI driver needs the phy driver).
>>
>> But still we cannot assume this is true for all platforms. OTG driver can also
>> be a separate entity than PHY driver.
>
> I understood it.
>
>>>>> I also wonder if array of hcd may be good for both xHCI and EHCI/OHCI.
>>>>> For example of xHCI:
>>>>> - otg->hcds[0] = primary_hcd
>>>>> - otg->hcds[1] = shared_hcd
>>>>>
>>>>> For example of EHCI/OHCI:
>>>>> - otg->hcds[0] = primary_hcd of EHCI
>>>>> - otg->hcds[1] = primary_hcd of OHCI
>>>>
>>>> The bigger problem is that how do we know in the OHCI/EHCI case that we need to wait
>>>> for both of them before starting the OTG FSM?
>>>> Some systems might use just OHCI or just EHCI.
>>>>
>>>> There is no guarantee that OTG driver registers before the HCDs so this piece
>>>> of information must come from the HCD itself. i.e. whether it needs a companion or not.
>>>
>>> I understood these problems. I wonder if my patch can resolve these problems.
>>>
>>> Best regards,
>>> Yoshihiro Shimoda
>>> ---
>>> diff --git a/Documentation/devicetree/bindings/usb/generic.txt b/Documentation/devicetree/bindings/usb/generic.txt
>>> index f6866c1..518b9eb 100644
>>> --- a/Documentation/devicetree/bindings/usb/generic.txt
>>> +++ b/Documentation/devicetree/bindings/usb/generic.txt
>>> @@ -27,6 +27,12 @@ Optional properties:
>>> - otg-controller: phandle to otg controller. Host or gadget controllers can
>>> contain this property to link it to a particular OTG
>>> controller.
>>> + - otg-hcds: phandle to host controller(s). An OTG controller can contain this
>>> + property. The first one is as "primary", and (optional) the second
>>> + one is as "shared".
>>> + - For example for xHCI: otg-hcds = <&xhci>, <&xhci>;
>>> + - For example for EHCI/OHCI: otg-hcds = <&ehci>, <&ohci>;
>>> + - For example for just EHCI: otg-hcds = <&ehci>;
>>>
>>
>> This is kind of duplicating the information. We are already specifying in the
>> hcd nodes as to which otg controller it is linked to.
>>
>> Instead, how about just adding a boolean property in the otg node saying that
>> HCD needs companion. e.g.
>> hcd-needs-compainion: must be present if otg controller is dealing with
>> EHCI host controller that needs a companion OHCI host controller.
>>
>> This can also be added in struct usb_otg_config so that it can deal with non-DT cases.
>>
>> So if this property is true, the OTG core will wait for 2 primary HCDs to be registered.
>
> Thank you for your suggestion. I agree with you.
> So, I made such a patch which. It is simple than my previous patch.
> What do you think?
>
> Best regards,
> Yoshihiro Shimoda
>
> ---
> Documentation/devicetree/bindings/usb/generic.txt | 3 +++
> drivers/usb/common/common.c | 2 ++
> drivers/usb/common/usb-otg.c | 11 +++++++----
> include/linux/usb/otg.h | 2 ++
> 4 files changed, 14 insertions(+), 4 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/common.c b/drivers/usb/common/common.c
> index e3d0161..8b74715 100644
> --- a/drivers/usb/common/common.c
> +++ b/drivers/usb/common/common.c
> @@ -233,6 +233,8 @@ int of_usb_update_otg_caps(struct device_node *np,
> if (of_find_property(np, "adp-disable", NULL) ||
> (otg_caps->otg_rev < 0x0200))
> otg_caps->adp_support = false;
> + if (of_find_property(np, "hcd-needs-companion", NULL))
> + otg_caps->needs_companion = true;

I'm not sure if otg_caps structure is a right place for this. Maybe Peter can confirm
if this is OK or not.

I was thinking more about adding this bit in the otg_config structure.

>
> return 0;
> }
> diff --git a/drivers/usb/common/usb-otg.c b/drivers/usb/common/usb-otg.c
> index 41e762a..e0df839 100644
> --- a/drivers/usb/common/usb-otg.c
> +++ b/drivers/usb/common/usb-otg.c
> @@ -823,13 +823,15 @@ 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->caps->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->caps->needs_companion ||
> + (hcd->shared_hcd == otg->primary_hcd.hcd)) {
> if (otg->shared_hcd.hcd) {
> dev_err(otg_dev, "otg: shared host already registered\n");
> goto err;
> @@ -865,7 +867,8 @@ 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->caps->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 */
>

These changes look good to me. Thanks.

> diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h
> index b094352..64a7db8 100644
> --- a/include/linux/usb/otg.h
> +++ b/include/linux/usb/otg.h
> @@ -112,12 +112,14 @@ struct usb_otg {
> * @hnp_support: Indicates if the device supports HNP.
> * @srp_support: Indicates if the device supports SRP.
> * @adp_support: Indicates if the device supports ADP.
> + * @needs_companion: Indicates if the device needs a companion controller.

Description is not exact. How about this.
"Indicates if host controller needs a companion controller"

Is hcd_needs_companion is better than just needs_companion?


> */
> struct usb_otg_caps {
> u16 otg_rev;
> bool hnp_support;
> bool srp_support;
> bool adp_support;
> + bool needs_companion;
> };
>
> /**
>

cheers,
-roger

2016-04-14 11:15:25

by Yoshihiro Shimoda

[permalink] [raw]
Subject: RE: [PATCH v6 07/12] usb: otg: add OTG/dual-role core

Hi,

> From: Roger Quadros
> Sent: Thursday, April 14, 2016 8:00 PM
>
> On 14/04/16 11:36, Yoshihiro Shimoda wrote:
> > Hi,
> >
< snip >
> > diff --git a/drivers/usb/common/common.c b/drivers/usb/common/common.c
> > index e3d0161..8b74715 100644
> > --- a/drivers/usb/common/common.c
> > +++ b/drivers/usb/common/common.c
> > @@ -233,6 +233,8 @@ int of_usb_update_otg_caps(struct device_node *np,
> > if (of_find_property(np, "adp-disable", NULL) ||
> > (otg_caps->otg_rev < 0x0200))
> > otg_caps->adp_support = false;
> > + if (of_find_property(np, "hcd-needs-companion", NULL))
> > + otg_caps->needs_companion = true;
>
> I'm not sure if otg_caps structure is a right place for this. Maybe Peter can confirm
> if this is OK or not.
>
> I was thinking more about adding this bit in the otg_config structure.

I see. I also think the otg_config is more suitable.

> >
> > return 0;
> > }
> > diff --git a/drivers/usb/common/usb-otg.c b/drivers/usb/common/usb-otg.c
> > index 41e762a..e0df839 100644
> > --- a/drivers/usb/common/usb-otg.c
> > +++ b/drivers/usb/common/usb-otg.c
> > @@ -823,13 +823,15 @@ 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->caps->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->caps->needs_companion ||
> > + (hcd->shared_hcd == otg->primary_hcd.hcd)) {
> > if (otg->shared_hcd.hcd) {
> > dev_err(otg_dev, "otg: shared host already registered\n");
> > goto err;
> > @@ -865,7 +867,8 @@ 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->caps->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 */
> >
>
> These changes look good to me. Thanks.

Thank you for the comment.
If we change the "needs_companion" place to the otg_config,
do we need to add a flag into the otg, instead of otg->caps?

> > diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h
> > index b094352..64a7db8 100644
> > --- a/include/linux/usb/otg.h
> > +++ b/include/linux/usb/otg.h
> > @@ -112,12 +112,14 @@ struct usb_otg {
> > * @hnp_support: Indicates if the device supports HNP.
> > * @srp_support: Indicates if the device supports SRP.
> > * @adp_support: Indicates if the device supports ADP.
> > + * @needs_companion: Indicates if the device needs a companion controller.
>
> Description is not exact. How about this.
> "Indicates if host controller needs a companion controller"
>
> Is hcd_needs_companion is better than just needs_companion?

I agree with you.

So, I will modify my local patch and test it tomorrow.

Best regards,
Yoshihiro Shimoda

>
> > */
> > struct usb_otg_caps {
> > u16 otg_rev;
> > bool hnp_support;
> > bool srp_support;
> > bool adp_support;
> > + bool needs_companion;
> > };
> >
> > /**
> >
>
> cheers,
> -roger

2016-04-14 11:32:29

by Roger Quadros

[permalink] [raw]
Subject: Re: [PATCH v6 07/12] usb: otg: add OTG/dual-role core

On 14/04/16 14:15, Yoshihiro Shimoda wrote:
> Hi,
>
>> From: Roger Quadros
>> Sent: Thursday, April 14, 2016 8:00 PM
>>
>> On 14/04/16 11:36, Yoshihiro Shimoda wrote:
>>> Hi,
>>>
> < snip >
>>> diff --git a/drivers/usb/common/common.c b/drivers/usb/common/common.c
>>> index e3d0161..8b74715 100644
>>> --- a/drivers/usb/common/common.c
>>> +++ b/drivers/usb/common/common.c
>>> @@ -233,6 +233,8 @@ int of_usb_update_otg_caps(struct device_node *np,
>>> if (of_find_property(np, "adp-disable", NULL) ||
>>> (otg_caps->otg_rev < 0x0200))
>>> otg_caps->adp_support = false;
>>> + if (of_find_property(np, "hcd-needs-companion", NULL))
>>> + otg_caps->needs_companion = true;
>>
>> I'm not sure if otg_caps structure is a right place for this. Maybe Peter can confirm
>> if this is OK or not.
>>
>> I was thinking more about adding this bit in the otg_config structure.
>
> I see. I also think the otg_config is more suitable.
>
>>>
>>> return 0;
>>> }
>>> diff --git a/drivers/usb/common/usb-otg.c b/drivers/usb/common/usb-otg.c
>>> index 41e762a..e0df839 100644
>>> --- a/drivers/usb/common/usb-otg.c
>>> +++ b/drivers/usb/common/usb-otg.c
>>> @@ -823,13 +823,15 @@ 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->caps->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->caps->needs_companion ||
>>> + (hcd->shared_hcd == otg->primary_hcd.hcd)) {
>>> if (otg->shared_hcd.hcd) {
>>> dev_err(otg_dev, "otg: shared host already registered\n");
>>> goto err;
>>> @@ -865,7 +867,8 @@ 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->caps->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 */
>>>
>>
>> These changes look good to me. Thanks.
>
> Thank you for the comment.
> If we change the "needs_companion" place to the otg_config,
> do we need to add a flag into the otg, instead of otg->caps?

Yes we can add a flag in struct usb_otg.

cheers,
-roger

>
>>> diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h
>>> index b094352..64a7db8 100644
>>> --- a/include/linux/usb/otg.h
>>> +++ b/include/linux/usb/otg.h
>>> @@ -112,12 +112,14 @@ struct usb_otg {
>>> * @hnp_support: Indicates if the device supports HNP.
>>> * @srp_support: Indicates if the device supports SRP.
>>> * @adp_support: Indicates if the device supports ADP.
>>> + * @needs_companion: Indicates if the device needs a companion controller.
>>
>> Description is not exact. How about this.
>> "Indicates if host controller needs a companion controller"
>>
>> Is hcd_needs_companion is better than just needs_companion?
>
> I agree with you.
>
> So, I will modify my local patch and test it tomorrow.
>
> Best regards,
> Yoshihiro Shimoda
>
>>
>>> */
>>> struct usb_otg_caps {
>>> u16 otg_rev;
>>> bool hnp_support;
>>> bool srp_support;
>>> bool adp_support;
>>> + bool needs_companion;
>>> };
>>>
>>> /**
>>>
>>
>> cheers,
>> -roger

2016-04-15 09:32:53

by Peter Chen

[permalink] [raw]
Subject: Re: [PATCH v6 07/12] usb: otg: add OTG/dual-role core

On Tue, Apr 05, 2016 at 05:05:12PM +0300, Roger Quadros wrote:
> + * usb_otg_register() - Register the OTG/dual-role device to OTG core
> + * @dev: OTG/dual-role controller device.
> + * @config: OTG configuration.
> + *
> + * Registers the OTG/dual-role controller device with the USB OTG core.
> + *
> + * Return: struct usb_otg * if success, ERR_PTR() if error.
> + */
> +struct usb_otg *usb_otg_register(struct device *dev,
> + struct usb_otg_config *config)
> +{
> + struct usb_otg *otg;
> + struct otg_wait_data *wait;
> + int ret = 0;
> +
> + if (!dev || !config || !config->fsm_ops)
> + return ERR_PTR(-EINVAL);
> +
> + /* already in list? */
> + mutex_lock(&otg_list_mutex);
> + if (usb_otg_get_data(dev)) {
> + dev_err(dev, "otg: %s: device already in otg list\n",
> + __func__);
> + ret = -EINVAL;
> + goto unlock;
> + }
> +
> + /* allocate and add to list */
> + otg = kzalloc(sizeof(*otg), GFP_KERNEL);
> + if (!otg) {
> + ret = -ENOMEM;
> + goto unlock;
> + }
> +
> + otg->dev = dev;
> + otg->caps = config->otg_caps;
> +
> + if ((otg->caps->hnp_support || otg->caps->srp_support ||
> + otg->caps->adp_support) && !config->otg_work)
> + dev_info(dev, "otg: limiting to dual-role\n");

What does above mean? Customized otg_work item may be dual-role,
may be full otg.

> +
> + if (config->otg_work) /* custom otg_work ? */
> + INIT_WORK(&otg->work, config->otg_work);
> + else
> + INIT_WORK(&otg->work, usb_otg_work);
> +
> + otg->wq = create_singlethread_workqueue("usb_otg");
> + if (!otg->wq) {
> + dev_err(dev, "otg: %s: can't create workqueue\n",
> + __func__);
> + ret = -ENOMEM;
> + goto err_wq;
> + }

I found a bug that caused by non-freezable workqueue, change it
as freezable please.

https://marc.ttias.be/linux-stable/2016-03/msg00723.php

I will review the whole set from next week.

Peter

> +
> + /* 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);
> +
> + /* were we in wait list? */
> + mutex_lock(&wait_list_mutex);
> + wait = usb_otg_get_wait(dev);
> + mutex_unlock(&wait_list_mutex);
> + if (wait) {
> + /* register pending host/gadget and flush from list */
> + usb_otg_flush_wait(dev);
> + }
> +
> + return otg;
> +
> +err_wq:
> + kfree(otg);
> +unlock:
> + mutex_unlock(&otg_list_mutex);
> + return ERR_PTR(ret);
> +}
> +EXPORT_SYMBOL_GPL(usb_otg_register);
> +
> +/**
> + * usb_otg_unregister() - Unregister the OTG/dual-role device from USB OTG core
> + * @dev: OTG controller device.
> + *
> + * Unregisters the OTG/dual-role controller device from USB OTG core.
> + * Prevents unregistering till both the associated Host and Gadget controllers
> + * have unregistered from the OTG core.
> + *
> + * Return: 0 on success, error value otherwise.
> + */
> +int usb_otg_unregister(struct device *dev)
> +{
> + struct usb_otg *otg;
> +
> + mutex_lock(&otg_list_mutex);
> + otg = usb_otg_get_data(dev);
> + if (!otg) {
> + dev_err(dev, "otg: %s: device not in otg list\n",
> + __func__);
> + mutex_unlock(&otg_list_mutex);
> + return -EINVAL;
> + }
> +
> + /* prevent unregister till both host & gadget have unregistered */
> + if (otg->host || otg->gadget) {
> + dev_err(dev, "otg: %s: host/gadget still registered\n",
> + __func__);
> + return -EBUSY;
> + }
> +
> + /* OTG FSM is halted when host/gadget unregistered */
> + destroy_workqueue(otg->wq);
> +
> + /* remove from otg list */
> + list_del(&otg->list);
> + kfree(otg);
> + mutex_unlock(&otg_list_mutex);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(usb_otg_unregister);
> +
> +/**
> + * start/kick the OTG FSM if we can
> + * fsm->lock must be held
> + */
> +static void usb_otg_start_fsm(struct usb_otg *otg)
> +{
> + struct otg_fsm *fsm = &otg->fsm;
> +
> + if (fsm->running)
> + goto kick_fsm;
> +
> + if (!otg->host) {
> + dev_info(otg->dev, "otg: can't start till host registers\n");
> + return;
> + }
> +
> + if (!otg->gadget) {
> + dev_info(otg->dev, "otg: can't start till gadget registers\n");
> + return;
> + }
> +
> + fsm->running = true;
> +kick_fsm:
> + queue_work(otg->wq, &otg->work);
> +}
> +
> +/**
> + * stop the OTG FSM. Stops Host & Gadget controllers as well.
> + * fsm->lock must be held
> + */
> +static void usb_otg_stop_fsm(struct usb_otg *otg)
> +{
> + struct otg_fsm *fsm = &otg->fsm;
> +
> + if (!fsm->running)
> + return;
> +
> + /* no more new events queued */
> + fsm->running = false;
> +
> + flush_workqueue(otg->wq);
> + otg->state = OTG_STATE_UNDEFINED;
> +
> + /* stop host/gadget immediately */
> + if (fsm->protocol == PROTO_HOST)
> + otg_start_host(otg, 0);
> + else if (fsm->protocol == PROTO_GADGET)
> + otg_start_gadget(otg, 0);
> + fsm->protocol = PROTO_UNDEF;
> +}
> +
> +/**
> + * usb_otg_sync_inputs - Sync OTG inputs with the OTG state machine
> + * @fsm: OTG FSM instance
> + *
> + * Used by the OTG driver to update the inputs to the OTG
> + * state machine.
> + *
> + * Can be called in IRQ context.
> + */
> +void usb_otg_sync_inputs(struct usb_otg *otg)
> +{
> + /* Don't kick FSM till it has started */
> + if (!otg->fsm.running)
> + return;
> +
> + /* Kick FSM */
> + queue_work(otg->wq, &otg->work);
> +}
> +EXPORT_SYMBOL_GPL(usb_otg_sync_inputs);
> +
> +/**
> + * usb_otg_kick_fsm - Kick the OTG state machine
> + * @otg_dev: OTG controller device
> + *
> + * Used by USB host/device stack to sync OTG related
> + * events to the OTG state machine.
> + * e.g. change in host_bus->b_hnp_enable, gadget->b_hnp_enable
> + *
> + * Returns: 0 on success, error value otherwise.
> + */
> +int usb_otg_kick_fsm(struct device *otg_dev)
> +{
> + struct usb_otg *otg;
> +
> + mutex_lock(&otg_list_mutex);
> + otg = usb_otg_get_data(otg_dev);
> + mutex_unlock(&otg_list_mutex);
> + if (!otg) {
> + dev_dbg(otg_dev, "otg: %s: invalid otg device\n",
> + __func__);
> + return -ENODEV;
> + }
> +
> + usb_otg_sync_inputs(otg);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(usb_otg_kick_fsm);
> +
> +/**
> + * usb_otg_register_hcd - Register the host controller to OTG core
> + * @hcd: host controller device
> + * @irqnum: interrupt number
> + * @irqflags: interrupt flags
> + * @ops: HCD ops to interface with the HCD
> + *
> + * This is used by the USB Host stack to register the host controller
> + * to the OTG core. Host controller must not be started by the
> + * caller as it is left upto the OTG state machine to do so.
> + * hcd->otg_dev must contain the related otg controller device.
> + *
> + * Returns: 0 on success, error value otherwise.
> + */
> +int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
> + unsigned long irqflags, struct otg_hcd_ops *ops)
> +{
> + struct usb_otg *otg;
> + struct device *hcd_dev = hcd->self.controller;
> + struct device *otg_dev = hcd->otg_dev;
> +
> + if (!otg_dev)
> + return -EINVAL;
> +
> + /* we're otg but otg controller might not yet be registered */
> + mutex_lock(&otg_list_mutex);
> + otg = usb_otg_get_data(otg_dev);
> + mutex_unlock(&otg_list_mutex);
> + if (!otg) {
> + dev_dbg(hcd_dev,
> + "otg: controller not yet registered. waiting..\n");
> + /*
> + * otg controller might register later. Put the hcd in
> + * wait list and call us back when ready
> + */
> + if (usb_otg_hcd_wait_add(otg_dev, hcd, irqnum, irqflags, ops)) {
> + dev_err(hcd_dev, "otg: failed to add hcd to wait list\n");
> + return -EINVAL;
> + }
> +
> + return 0;
> + }
> +
> + /* 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) {
> + /* are we in wait list? */
> + if (!usb_otg_hcd_wait_remove(hcd))
> + return 0;
> +
> + dev_dbg(hcd_dev, "otg: host wasn't registered with otg\n");
> + return -EINVAL;
> + }
> +
> + mutex_lock(&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 {
> + dev_err(otg_dev, "otg: host %s wasn't registered with otg\n",
> + dev_name(hcd_dev));
> + mutex_unlock(&otg->fsm.lock);
> + return -EINVAL;
> + }
> +
> + /* stop FSM & Host */
> + usb_otg_stop_fsm(otg);
> + otg->host = NULL;
> +
> + mutex_unlock(&otg->fsm.lock);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(usb_otg_unregister_hcd);
> +
> +/**
> + * usb_otg_register_gadget - Register the gadget controller to OTG core
> + * @gadget: gadget controller
> + *
> + * This is used by the USB gadget stack to register the gadget controller
> + * to the OTG core. Gadget controller must not be started by the
> + * caller as it is left upto the OTG state machine to do so.
> + *
> + * Gadget core must call this only when all resources required for
> + * gadget controller to run are available.
> + * i.e. gadget function driver is available.
> + *
> + * Returns: 0 on success, error value otherwise.
> + */
> +int usb_otg_register_gadget(struct usb_gadget *gadget,
> + struct otg_gadget_ops *ops)
> +{
> + struct usb_otg *otg;
> + struct device *gadget_dev = &gadget->dev;
> + struct device *otg_dev = gadget->otg_dev;
> +
> + if (!otg_dev)
> + return -EINVAL; /* we're definitely not OTG */
> +
> + /* we're otg but otg controller might not yet be registered */
> + mutex_lock(&otg_list_mutex);
> + otg = usb_otg_get_data(otg_dev);
> + mutex_unlock(&otg_list_mutex);
> + if (!otg) {
> + dev_dbg(gadget_dev,
> + "otg: controller not yet registered. waiting..\n");
> + /*
> + * otg controller might register later. Put the gadget in
> + * wait list and call us back when ready
> + */
> + if (usb_otg_gadget_wait_add(otg_dev, gadget, ops)) {
> + dev_err(gadget_dev,
> + "otg: failed to add to gadget to wait list\n");
> + return -EINVAL;
> + }
> +
> + return 0;
> + }
> +
> + 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));
> +
> + /* start FSM */
> + usb_otg_start_fsm(otg);
> + mutex_unlock(&otg->fsm.lock);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(usb_otg_register_gadget);
> +
> +/**
> + * usb_otg_unregister_gadget - Unregister the gadget controller from OTG core
> + * @gadget: gadget controller
> + *
> + * This is used by the USB gadget stack to unregister the gadget controller
> + * from the OTG core. Ensures that gadget controller is halted
> + * on successful return.
> + *
> + * Returns: 0 on success, error value otherwise.
> + */
> +int usb_otg_unregister_gadget(struct usb_gadget *gadget)
> +{
> + struct usb_otg *otg;
> + struct device *gadget_dev = &gadget->dev;
> + struct device *otg_dev = gadget->otg_dev;
> +
> + if (!otg_dev)
> + return -EINVAL;
> +
> + mutex_lock(&otg_list_mutex);
> + otg = usb_otg_get_data(otg_dev);
> + mutex_unlock(&otg_list_mutex);
> + if (!otg) {
> + /* are we in wait list? */
> + if (!usb_otg_gadget_wait_remove(gadget))
> + return 0;
> +
> + dev_dbg(gadget_dev, "otg: gadget wasn't registered with otg\n");
> + return -EINVAL;
> + }
> +
> + mutex_lock(&otg->fsm.lock);
> + if (otg->gadget != gadget) {
> + dev_err(otg_dev, "otg: gadget %s wasn't registered with otg\n",
> + dev_name(&gadget->dev));
> + mutex_unlock(&otg->fsm.lock);
> + return -EINVAL;
> + }
> +
> + /* Stop FSM & gadget */
> + 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);
> diff --git a/drivers/usb/common/usb-otg.h b/drivers/usb/common/usb-otg.h
> new file mode 100644
> index 0000000..2bf3fbf
> --- /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) 2016 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 ae228d0..b468a9f 100644
> --- a/drivers/usb/core/Kconfig
> +++ b/drivers/usb/core/Kconfig
> @@ -42,7 +42,7 @@ config USB_DYNAMIC_MINORS
> If you are unsure about this, say N here.
>
> config USB_OTG
> - bool "OTG support"
> + bool "OTG/Dual-role support"
> depends on PM
> default n
> help
> diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h
> index 8c0ae64..1878ae1 100644
> --- a/include/linux/usb/gadget.h
> +++ b/include/linux/usb/gadget.h
> @@ -583,6 +583,7 @@ struct usb_gadget_ops {
> * @out_epnum: last used out ep number
> * @in_epnum: last used in ep number
> * @otg_caps: OTG capabilities of this gadget.
> + * @otg_dev: OTG controller device, if needs to be used with OTG core.
> * @sg_supported: true if we can handle scatter-gather
> * @is_otg: True if the USB device port uses a Mini-AB jack, so that the
> * gadget driver must provide a USB OTG descriptor.
> @@ -639,6 +640,7 @@ struct usb_gadget {
> unsigned out_epnum;
> unsigned in_epnum;
> struct usb_otg_caps *otg_caps;
> + struct device *otg_dev;
>
> unsigned sg_supported:1;
> unsigned is_otg:1;
> diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h
> index 861ccaa..2017cd4 100644
> --- a/include/linux/usb/hcd.h
> +++ b/include/linux/usb/hcd.h
> @@ -184,6 +184,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 36f0cf9..ba6755c 100644
> --- a/include/linux/usb/otg-fsm.h
> +++ b/include/linux/usb/otg-fsm.h
> @@ -61,6 +61,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
> @@ -133,6 +138,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 */
> @@ -188,6 +194,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..b094352 100644
> --- a/include/linux/usb/otg.h
> +++ b/include/linux/usb/otg.h
> @@ -10,10 +10,55 @@
> #define __LINUX_USB_OTG_H
>
> #include <linux/phy/phy.h>
> -#include <linux/usb/phy.h>
> -#include <linux/usb/otg-fsm.h>
> +#include <linux/device.h>
> +#include <linux/hrtimer.h>
> +#include <linux/ktime.h>
> +#include <linux/usb.h>
> #include <linux/usb/hcd.h>
> +#include <linux/usb/gadget.h>
> +#include <linux/usb/otg-fsm.h>
> +#include <linux/usb/phy.h>
>
> +/**
> + * struct otg_hcd - host controller state and interface
> + *
> + * @hcd: host controller
> + * @irqnum: irq number
> + * @irqflags: irq flags
> + * @ops: otg to host controller interface
> + * @ops: otg to host controller interface
> + * @otg_dev: otg controller device
> + */
> +struct otg_hcd {
> + struct usb_hcd *hcd;
> + unsigned int irqnum;
> + unsigned long irqflags;
> + struct otg_hcd_ops *ops;
> + struct device *otg_dev;
> +};
> +
> +/**
> + * struct usb_otg - 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 +69,24 @@ 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;
> + 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,7 +102,7 @@ struct usb_otg {
>
> /* start or continue HNP role switch */
> int (*start_hnp)(struct usb_otg *otg);
> -
> +/*---------------------------------------------------------------*/
> };
>
> /**
> @@ -60,8 +120,92 @@ struct usb_otg_caps {
> bool adp_support;
> };
>
> +/**
> + * struct usb_otg_config - otg controller configuration
> + * @caps: otg capabilities of the controller
> + * @ops: otg fsm operations
> + * @otg_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);
> +};
> +
> 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);
> +
> +#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;
> +}
> +#endif /* CONFIG_USB_OTG */
> +
> +/*------------- deprecated interface -----------------------------*/
> /* Context: can sleep */
> static inline int
> otg_start_hnp(struct usb_otg *otg)
> @@ -113,6 +257,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 +383,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.5.0
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-usb" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html

--

Best Regards,
Peter Chen

2016-04-15 09:59:42

by Yoshihiro Shimoda

[permalink] [raw]
Subject: RE: [PATCH v6 07/12] usb: otg: add OTG/dual-role core

Hi,

> From: Roger Quadros
> Sent: Thursday, April 14, 2016 8:32 PM
>
> On 14/04/16 14:15, Yoshihiro Shimoda wrote:
> > Hi,
> >
< snip >
> >>> @@ -865,7 +867,8 @@ 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->caps->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 */
> >>>
> >>
> >> These changes look good to me. Thanks.
> >
> > Thank you for the comment.
> > If we change the "needs_companion" place to the otg_config,
> > do we need to add a flag into the otg, instead of otg->caps?
>
> Yes we can add a flag in struct usb_otg.

Thank you for the comment.

I made a fixed patch.
So, should I send this patch to ML after you sent v7 patches?
Or, would you apply this patch before you send v7 patches?

Best regards,
Yoshihiro Shimoda

---


2016-04-15 10:03:24

by Yoshihiro Shimoda

[permalink] [raw]
Subject: RE: [PATCH v6 07/12] usb: otg: add OTG/dual-role core

Hi,

> From: Yoshihiro Shimoda
> Sent: Friday, April 15, 2016 6:59 PM
>
> Hi,
>
> > From: Roger Quadros
> > Sent: Thursday, April 14, 2016 8:32 PM
> >
> > On 14/04/16 14:15, Yoshihiro Shimoda wrote:
> > > Hi,
> > >
> < snip >
> > >>> @@ -865,7 +867,8 @@ 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->caps->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 */
> > >>>
> > >>
> > >> These changes look good to me. Thanks.
> > >
> > > Thank you for the comment.
> > > If we change the "needs_companion" place to the otg_config,
> > > do we need to add a flag into the otg, instead of otg->caps?
> >
> > Yes we can add a flag in struct usb_otg.
>
> Thank you for the comment.
>
> I made a fixed patch.
> So, should I send this patch to ML after you sent v7 patches?
> Or, would you apply this patch before you send v7 patches?

Oops, I sent this email without my patch...

---
Subject: [PATCH] usb: otg: add hcd companion support

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 <[email protected]>
---
Documentation/devicetree/bindings/usb/generic.txt | 3 +++
drivers/usb/common/usb-otg.c | 17 +++++++++++++----
include/linux/usb/otg.h | 7 ++++++-
3 files changed, 22 insertions(+), 5 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 41e762a..83c8c96 100644
--- a/drivers/usb/common/usb-otg.c
+++ b/drivers/usb/common/usb-otg.c
@@ -20,6 +20,7 @@
#include <linux/list.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>
@@ -600,6 +601,10 @@ struct usb_otg *usb_otg_register(struct device *dev,
else
INIT_WORK(&otg->work, usb_otg_work);

+ if (of_find_property(dev->of_node, "hcd-needs-companion", NULL) ||
+ config->hcd_needs_companion) /* needs comanion ? */
+ otg->flags |= OTG_FLAG_HCD_NEEDS_COMPANION;
+
otg->wq = create_singlethread_workqueue("usb_otg");
if (!otg->wq) {
dev_err(dev, "otg: %s: can't create workqueue\n",
@@ -823,13 +828,15 @@ 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");
goto err;
@@ -865,7 +872,9 @@ 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 */

diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h
index b094352..6f4ca77 100644
--- a/include/linux/usb/otg.h
+++ b/include/linux/usb/otg.h
@@ -57,7 +57,8 @@ struct otg_hcd {
* @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;
@@ -84,6 +85,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 -----------------------------*/
@@ -125,11 +127,14 @@ struct usb_otg_caps {
* @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);
--
1.9.1

2016-04-15 10:58:25

by Roger Quadros

[permalink] [raw]
Subject: Re: [PATCH v6 07/12] usb: otg: add OTG/dual-role core

On 15/04/16 12:59, Yoshihiro Shimoda wrote:
> Hi,
>
>> From: Roger Quadros
>> Sent: Thursday, April 14, 2016 8:32 PM
>>
>> On 14/04/16 14:15, Yoshihiro Shimoda wrote:
>>> Hi,
>>>
> < snip >
>>>>> @@ -865,7 +867,8 @@ 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->caps->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 */
>>>>>
>>>>
>>>> These changes look good to me. Thanks.
>>>
>>> Thank you for the comment.
>>> If we change the "needs_companion" place to the otg_config,
>>> do we need to add a flag into the otg, instead of otg->caps?
>>
>> Yes we can add a flag in struct usb_otg.
>
> Thank you for the comment.
>
> I made a fixed patch.
> So, should I send this patch to ML after you sent v7 patches?
> Or, would you apply this patch before you send v7 patches?
>

Thanks for working on this and testing the series. I will pick your patch
as part of v7.

cheers,
-roger

2016-04-15 11:01:08

by Roger Quadros

[permalink] [raw]
Subject: Re: [PATCH v6 07/12] usb: otg: add OTG/dual-role core

On 15/04/16 12:25, Peter Chen wrote:
> On Tue, Apr 05, 2016 at 05:05:12PM +0300, Roger Quadros wrote:
>> + * usb_otg_register() - Register the OTG/dual-role device to OTG core
>> + * @dev: OTG/dual-role controller device.
>> + * @config: OTG configuration.
>> + *
>> + * Registers the OTG/dual-role controller device with the USB OTG core.
>> + *
>> + * Return: struct usb_otg * if success, ERR_PTR() if error.
>> + */
>> +struct usb_otg *usb_otg_register(struct device *dev,
>> + struct usb_otg_config *config)
>> +{
>> + struct usb_otg *otg;
>> + struct otg_wait_data *wait;
>> + int ret = 0;
>> +
>> + if (!dev || !config || !config->fsm_ops)
>> + return ERR_PTR(-EINVAL);
>> +
>> + /* already in list? */
>> + mutex_lock(&otg_list_mutex);
>> + if (usb_otg_get_data(dev)) {
>> + dev_err(dev, "otg: %s: device already in otg list\n",
>> + __func__);
>> + ret = -EINVAL;
>> + goto unlock;
>> + }
>> +
>> + /* allocate and add to list */
>> + otg = kzalloc(sizeof(*otg), GFP_KERNEL);
>> + if (!otg) {
>> + ret = -ENOMEM;
>> + goto unlock;
>> + }
>> +
>> + otg->dev = dev;
>> + otg->caps = config->otg_caps;
>> +
>> + if ((otg->caps->hnp_support || otg->caps->srp_support ||
>> + otg->caps->adp_support) && !config->otg_work)
>> + dev_info(dev, "otg: limiting to dual-role\n");
>
> What does above mean? Customized otg_work item may be dual-role,
> may be full otg.

I'm checking for !config->otg_work so we're sure of using the
default dual-role only work function.

>
>> +
>> + if (config->otg_work) /* custom otg_work ? */
>> + INIT_WORK(&otg->work, config->otg_work);
>> + else
>> + INIT_WORK(&otg->work, usb_otg_work);
>> +
>> + otg->wq = create_singlethread_workqueue("usb_otg");
>> + if (!otg->wq) {
>> + dev_err(dev, "otg: %s: can't create workqueue\n",
>> + __func__);
>> + ret = -ENOMEM;
>> + goto err_wq;
>> + }
>
> I found a bug that caused by non-freezable workqueue, change it
> as freezable please.
>
> https://marc.ttias.be/linux-stable/2016-03/msg00723.php

OK.

>
> I will review the whole set from next week.

Thanks.

--
cheers,
-roger

2016-04-18 02:16:54

by Peter Chen

[permalink] [raw]
Subject: Re: [PATCH v6 07/12] usb: otg: add OTG/dual-role core

On Fri, Apr 15, 2016 at 02:00:46PM +0300, Roger Quadros wrote:
> On 15/04/16 12:25, Peter Chen wrote:
> > On Tue, Apr 05, 2016 at 05:05:12PM +0300, Roger Quadros wrote:
> >> + * usb_otg_register() - Register the OTG/dual-role device to OTG core
> >> + * @dev: OTG/dual-role controller device.
> >> + * @config: OTG configuration.
> >> + *
> >> + * Registers the OTG/dual-role controller device with the USB OTG core.
> >> + *
> >> + * Return: struct usb_otg * if success, ERR_PTR() if error.
> >> + */
> >> +struct usb_otg *usb_otg_register(struct device *dev,
> >> + struct usb_otg_config *config)
> >> +{
> >> + struct usb_otg *otg;
> >> + struct otg_wait_data *wait;
> >> + int ret = 0;
> >> +
> >> + if (!dev || !config || !config->fsm_ops)
> >> + return ERR_PTR(-EINVAL);
> >> +
> >> + /* already in list? */
> >> + mutex_lock(&otg_list_mutex);
> >> + if (usb_otg_get_data(dev)) {
> >> + dev_err(dev, "otg: %s: device already in otg list\n",
> >> + __func__);
> >> + ret = -EINVAL;
> >> + goto unlock;
> >> + }
> >> +
> >> + /* allocate and add to list */
> >> + otg = kzalloc(sizeof(*otg), GFP_KERNEL);
> >> + if (!otg) {
> >> + ret = -ENOMEM;
> >> + goto unlock;
> >> + }
> >> +
> >> + otg->dev = dev;
> >> + otg->caps = config->otg_caps;
> >> +
> >> + if ((otg->caps->hnp_support || otg->caps->srp_support ||
> >> + otg->caps->adp_support) && !config->otg_work)
> >> + dev_info(dev, "otg: limiting to dual-role\n");
> >
> > What does above mean? Customized otg_work item may be dual-role,
> > may be full otg.
>
> I'm checking for !config->otg_work so we're sure of using the
> default dual-role only work function.
>

I see. But whether it is dual-role or fully otg should depend on
otg->caps, the work item may be different according to design.
Besides, your code seems to depend on one of the otg capabilities
for dual-role.

--

Best Regards,
Peter Chen

2016-04-18 02:37:30

by Peter Chen

[permalink] [raw]
Subject: Re: [PATCH v6 01/12] usb: hcd: Initialize hcd->flags to 0

On Wed, Apr 06, 2016 at 09:32:22AM +0300, Roger Quadros wrote:
> On 06/04/16 09:09, Felipe Balbi wrote:
> >
> > Hi,
> >
> > Roger Quadros <[email protected]> writes:
> >> diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
> >> index 2ca2cef..6b1930d 100644
> >> --- a/drivers/usb/core/hcd.c
> >> +++ b/drivers/usb/core/hcd.c
> >> @@ -2706,6 +2706,7 @@ int usb_add_hcd(struct usb_hcd *hcd,
> >> int retval;
> >> struct usb_device *rhdev;
> >>
> >> + hcd->flags = 0;
> >
> > seems like this would make more sense in usb_del_hcd() instead.
> >
>
> OK, I'll move it there.
>

It depends on Alan's comments, whether only usb_add_hcd/usb_del_hcd
pair can be called repeat. If Alan acks it, I have no idea for it.

--

Best Regards,
Peter Chen

2016-04-18 06:37:02

by Peter Chen

[permalink] [raw]
Subject: Re: [PATCH v6 08/12] usb: hcd: Adapt to OTG core

On Tue, Apr 05, 2016 at 05:05:13PM +0300, Roger Quadros wrote:
> Introduce usb_otg_add/remove_hcd() for use by host
> controllers that are part of OTG/dual-role port.
>
> Non Device tree platforms can use the otg_dev argument
> to specify the OTG controller device. If otg_dev is NULL
> then the device tree node's otg-controller property is used to
> get the otg_dev device.
>
> Signed-off-by: Roger Quadros <[email protected]>
> ---
> drivers/usb/core/hcd.c | 71 +++++++++++++++++++++++++++++++++++++++++++++++++
> include/linux/usb/hcd.h | 4 +++
> 2 files changed, 75 insertions(+)
>
> diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
> index 6b1930d..6a80193 100644
> --- a/drivers/usb/core/hcd.c
> +++ b/drivers/usb/core/hcd.c
> @@ -46,6 +46,10 @@
> #include <linux/usb.h>
> #include <linux/usb/hcd.h>
> #include <linux/usb/phy.h>
> +#include <linux/usb/otg.h>
> +
> +#include <linux/of.h>
> +#include <linux/of_platform.h>
>
> #include "usb.h"
>
> @@ -3013,6 +3017,73 @@ void usb_remove_hcd(struct usb_hcd *hcd)
> }
> EXPORT_SYMBOL_GPL(usb_remove_hcd);
>
> +
> +static struct otg_hcd_ops otg_hcd_intf = {
> + .add = usb_add_hcd,
> + .remove = usb_remove_hcd,
> + .usb_bus_start_enum = usb_bus_start_enum,
> + .usb_control_msg = usb_control_msg,
> + .usb_hub_find_child = usb_hub_find_child,
> +};
> +
> +/**
> + * usb_otg_add_hcd - Register the HCD with OTG core.
> + * @hcd: the usb_hcd structure to initialize
> + * @irqnum: Interrupt line to allocate
> + * @irqflags: Interrupt type flags
> + * @otg_dev: OTG controller device manging this HCD

%s/manging/managing

> + *
> + * Registers the HCD with OTG core. OTG core will call usb_add_hcd()
> + * or usb_remove_hcd() as necessary.
> + * If otg_dev is NULL then device tree node is checked for OTG
> + * controller device via the otg-controller property.
> + */
> +int usb_otg_add_hcd(struct usb_hcd *hcd,
> + unsigned int irqnum, unsigned long irqflags,
> + struct device *otg_dev)
> +{

Some users may not want to use default otg_hcd_ops, would you please
add one more parameter (eg, hcd_ops) for that? If hcd_ops is NULL, just
use the default one.

> + struct device *dev = hcd->self.controller;
> +
> + if (!otg_dev) {
> + struct device_node *np;
> + struct platform_device *pdev;
> +
> + np = of_parse_phandle(dev->of_node, "otg-controller", 0);
> + if (!np) {
> + dev_err(dev,
> + "otg_dev is NULL and no otg-controller property in DT\n");
> + return -EINVAL;
> + }
> +
> + pdev = of_find_device_by_node(np);
> + of_node_put(np);
> + if (!pdev) {
> + dev_err(dev,
> + "couldn't get otg-controller device\n");
> + return -ENODEV;
> + }
> +
> + hcd->otg_dev = &pdev->dev;
> + } else {
> + hcd->otg_dev = otg_dev;
> + }
> +
> + return usb_otg_register_hcd(hcd, irqnum, irqflags, &otg_hcd_intf);
> +}
> +EXPORT_SYMBOL_GPL(usb_otg_add_hcd);
> +

--

Best Regards,
Peter Chen

2016-04-18 07:07:18

by Peter Chen

[permalink] [raw]
Subject: Re: [PATCH v6 09/12] usb: gadget: udc: adapt to OTG core

On Tue, Apr 05, 2016 at 05:05:14PM +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().
>
> Introduce usb_otg_add_gadget_udc() to allow controller drivers
> to register a gadget controller that is part of an OTG instance.
>
> Register with OTG core when gadget function driver
> is available and unregister when function driver is unbound.
>
> We need to unlock the usb_lock mutexbefore calling

...mutex before...

> usb_otg_register_gadget() in udc_bind_to_driver() and
> usb_gadget_remove_driver() else it will cause a circular
> locking dependency.
>
> Ignore softconnect sysfs control when we're in OTG
> mode as OTG FSM takes care of gadget softconnect using
> the b_bus_req mechanism.
>
> Signed-off-by: Roger Quadros <[email protected]>
> ---
> drivers/usb/gadget/udc/udc-core.c | 166 +++++++++++++++++++++++++++++++++++---
> include/linux/usb/gadget.h | 4 +
> 2 files changed, 161 insertions(+), 9 deletions(-)
>
> diff --git a/drivers/usb/gadget/udc/udc-core.c b/drivers/usb/gadget/udc/udc-core.c
> index 4151597..9b9702f 100644
> --- a/drivers/usb/gadget/udc/udc-core.c
> +++ b/drivers/usb/gadget/udc/udc-core.c
> @@ -28,6 +28,10 @@
> #include <linux/usb/ch9.h>
> #include <linux/usb/gadget.h>
> #include <linux/usb.h>
> +#include <linux/usb/otg.h>
> +
> +#include <linux/of.h>
> +#include <linux/of_platform.h>
>
> /**
> * struct usb_udc - describes one usb device controller
> @@ -304,6 +308,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);
> }

You may delete the debug message next time.

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

The same.

>
> /**
> + * usb_gadget_start - start the usb gadget controller and connect to bus
> + * @gadget: the gadget device to start
> + *
> + * This is external API for use by OTG core.
> + *
> + * Start the usb device controller and connect to bus (enable pull).
> + */
> +static int usb_gadget_start(struct usb_gadget *gadget)
> +{
> + int ret;
> + struct usb_udc *udc = NULL;
> +
> + dev_dbg(&gadget->dev, "%s\n", __func__);

The same

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

The same

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

The above finding gadget code is the same with usb_gadget_start, can we
use one common API to instead of it?

> +
> +found:
> + usb_gadget_disconnect(udc->gadget);
> + udc->driver->disconnect(udc->gadget);
> + usb_gadget_udc_stop(udc);
> + mutex_unlock(&udc_lock);
> +
> + return 0;
> +}
> +
> +/**
> * usb_udc_release - release the usb_udc struct
> * @dev: the dev member within usb_udc
> *
> @@ -486,6 +562,48 @@ int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget)
> }
> EXPORT_SYMBOL_GPL(usb_add_gadget_udc);
>
> +/**
> + * usb_otg_add_gadget_udc - adds a new gadget to the udc class driver list
> + * @parent: the parent device to this udc. Usually the controller
> + * driver's device.
> + * @gadget: the gadget to be added to the list
> + * @otg_dev: the OTG controller device
> + *
> + * If otg_dev is NULL then device tree node is checked
> + * for OTG controller via the otg-controller property.
> + * Returns zero on success, negative errno otherwise.
> + */
> +int usb_otg_add_gadget_udc(struct device *parent, struct usb_gadget *gadget,
> + struct device *otg_dev)
> +{
> + if (!otg_dev) {
> + struct device_node *np;
> + struct platform_device *pdev;
> +
> + np = of_parse_phandle(parent->of_node, "otg-controller", 0);
> + if (!np) {
> + dev_err(parent,
> + "otg_dev is NULL or no otg-controller property in DT\n");
> + return -EINVAL;
> + }
> +
> + pdev = of_find_device_by_node(np);
> + of_node_put(np);
> + if (!pdev) {
> + dev_err(parent, "couldn't get otg-controller device\n");
> + return -ENODEV;
> + }
> +
> + gadget->otg_dev = &pdev->dev;
> + } else {
> + gadget->otg_dev = otg_dev;
> + }

The above the code is duplicated with hcd's. Can we do something common
at usb-otg.c, eg define an API get_usb_otg_dev(struct usb_gadget *gadget, struct
usb_hcd *hcd), in this API we can try to get otg_dev for both gadget and
hcd, and we can call it at controller driver during register otg.

--

Best Regards,
Peter Chen

2016-04-18 07:16:08

by Peter Chen

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

On Tue, Apr 05, 2016 at 05:05:16PM +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.

Since we have still not added fsm.a_set_b_hnp_en in OTG FSM, and this
patch set is mainly for DRD, would you please move out this patch from
this set?

Peter

>
> Signed-off-by: Roger Quadros <[email protected]>
> ---
> drivers/usb/core/hub.c | 17 +++++++++++++++--
> 1 file changed, 15 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
> index 38cc4ba..27e3b4c 100644
> --- a/drivers/usb/core/hub.c
> +++ b/drivers/usb/core/hub.c
> @@ -2273,6 +2273,7 @@ static int usb_enumerate_device_otg(struct usb_device *udev)
> && udev->parent == udev->bus->root_hub) {
> struct usb_otg_descriptor *desc = NULL;
> struct usb_bus *bus = udev->bus;
> + struct usb_hcd *hcd = bus_to_hcd(bus);
> unsigned port1 = udev->portnum;
>
> /* descriptor may appear anywhere in config */
> @@ -2302,6 +2303,9 @@ static int usb_enumerate_device_otg(struct usb_device *udev)
> dev_err(&udev->dev, "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(hcd->otg_dev);
> }
> } else if (desc->bLength == sizeof
> (struct usb_otg_descriptor)) {
> @@ -2312,10 +2316,14 @@ static int usb_enumerate_device_otg(struct usb_device *udev)
> USB_DEVICE_A_ALT_HNP_SUPPORT,
> 0, NULL, 0,
> USB_CTRL_SET_TIMEOUT);
> - if (err < 0)
> + if (err < 0) {
> dev_err(&udev->dev,
> "set a_alt_hnp_support failed: %d\n",
> err);
> + } else {
> + /* notify OTG fsm about a_set_b_hnp_enable */
> + usb_otg_kick_fsm(hcd->otg_dev);
> + }
> }
> }
> #endif
> @@ -4355,8 +4363,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(hcd->otg_dev);
> +#endif
> + }
> }
>
> /* Some low speed devices have problems with the quick delay, so */
> --
> 2.5.0
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-usb" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html

--

Best Regards,
Peter Chen

2016-04-18 07:48:40

by Peter Chen

[permalink] [raw]
Subject: Re: [PATCH v6 02/12] usb: hcd.h: Add OTG to HCD interface

On Tue, Apr 05, 2016 at 05:05:07PM +0300, Roger Quadros wrote:
> The OTG core will use struct otg_hcd_ops to interface
> with the HCD controller.
>
> 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 <[email protected]>
> ---
> 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 b98f831..861ccaa 100644
> --- a/include/linux/usb/hcd.h
> +++ b/include/linux/usb/hcd.h
> @@ -399,6 +399,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 of 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.5.0
>

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

--

Best Regards,
Peter Chen

2016-04-18 07:49:26

by Peter Chen

[permalink] [raw]
Subject: Re: [PATCH v6 03/12] usb: otg-fsm: use usb_otg wherever possible

On Tue, Apr 05, 2016 at 05:05:08PM +0300, Roger Quadros wrote:
> Move otg_fsm into usb_otg and use usb_otg wherever possible
> in the usb_otg APIs.
>
> Signed-off-by: Roger Quadros <[email protected]>

Acked-by: Peter Chen <[email protected]>
> ---
> drivers/usb/chipidea/ci.h | 1 -
> drivers/usb/chipidea/core.c | 12 +--
> drivers/usb/chipidea/debug.c | 2 +-
> drivers/usb/chipidea/otg_fsm.c | 171 ++++++++++++++++++-------------------
> drivers/usb/chipidea/udc.c | 17 ++--
> drivers/usb/common/usb-otg-fsm.c | 180 ++++++++++++++++++++-------------------
> drivers/usb/phy/phy-fsl-usb.c | 143 +++++++++++++++----------------
> drivers/usb/phy/phy-fsl-usb.h | 3 +-
> include/linux/usb/otg-fsm.h | 132 +++-------------------------
> include/linux/usb/otg.h | 107 +++++++++++++++++++++++
> 10 files changed, 384 insertions(+), 384 deletions(-)
>
> diff --git a/drivers/usb/chipidea/ci.h b/drivers/usb/chipidea/ci.h
> index cd41455..c523975 100644
> --- a/drivers/usb/chipidea/ci.h
> +++ b/drivers/usb/chipidea/ci.h
> @@ -209,7 +209,6 @@ struct ci_hdrc {
> enum ci_role role;
> bool is_otg;
> struct usb_otg otg;
> - struct otg_fsm fsm;
> struct hrtimer otg_fsm_hrtimer;
> ktime_t hr_timeouts[NUM_OTG_FSM_TIMERS];
> unsigned enabled_otg_timer_bits;
> diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c
> index 69426e6..a5570a9 100644
> --- a/drivers/usb/chipidea/core.c
> +++ b/drivers/usb/chipidea/core.c
> @@ -1085,7 +1085,7 @@ static int ci_hdrc_remove(struct platform_device *pdev)
> /* Prepare wakeup by SRP before suspend */
> static void ci_otg_fsm_suspend_for_srp(struct ci_hdrc *ci)
> {
> - if ((ci->fsm.otg->state == OTG_STATE_A_IDLE) &&
> + if ((ci->otg.state == OTG_STATE_A_IDLE) &&
> !hw_read_otgsc(ci, OTGSC_ID)) {
> hw_write(ci, OP_PORTSC, PORTSC_W1C_BITS | PORTSC_PP,
> PORTSC_PP);
> @@ -1097,13 +1097,13 @@ static void ci_otg_fsm_suspend_for_srp(struct ci_hdrc *ci)
> /* Handle SRP when wakeup by data pulse */
> static void ci_otg_fsm_wakeup_by_srp(struct ci_hdrc *ci)
> {
> - if ((ci->fsm.otg->state == OTG_STATE_A_IDLE) &&
> - (ci->fsm.a_bus_drop == 1) && (ci->fsm.a_bus_req == 0)) {
> + if ((ci->otg.state == OTG_STATE_A_IDLE) &&
> + (ci->otg.fsm.a_bus_drop == 1) && (ci->otg.fsm.a_bus_req == 0)) {
> if (!hw_read_otgsc(ci, OTGSC_ID)) {
> - ci->fsm.a_srp_det = 1;
> - ci->fsm.a_bus_drop = 0;
> + ci->otg.fsm.a_srp_det = 1;
> + ci->otg.fsm.a_bus_drop = 0;
> } else {
> - ci->fsm.id = 1;
> + ci->otg.fsm.id = 1;
> }
> ci_otg_queue_work(ci);
> }
> diff --git a/drivers/usb/chipidea/debug.c b/drivers/usb/chipidea/debug.c
> index 6d23eed..374cdaa 100644
> --- a/drivers/usb/chipidea/debug.c
> +++ b/drivers/usb/chipidea/debug.c
> @@ -224,7 +224,7 @@ static int ci_otg_show(struct seq_file *s, void *unused)
> if (!ci || !ci_otg_is_fsm_mode(ci))
> return 0;
>
> - fsm = &ci->fsm;
> + fsm = &ci->otg.fsm;
>
> /* ------ State ----- */
> seq_printf(s, "OTG state: %s\n\n",
> diff --git a/drivers/usb/chipidea/otg_fsm.c b/drivers/usb/chipidea/otg_fsm.c
> index 5f169b3..f4e9fb5 100644
> --- a/drivers/usb/chipidea/otg_fsm.c
> +++ b/drivers/usb/chipidea/otg_fsm.c
> @@ -40,7 +40,7 @@ get_a_bus_req(struct device *dev, struct device_attribute *attr, char *buf)
>
> next = buf;
> size = PAGE_SIZE;
> - t = scnprintf(next, size, "%d\n", ci->fsm.a_bus_req);
> + t = scnprintf(next, size, "%d\n", ci->otg.fsm.a_bus_req);
> size -= t;
> next += t;
>
> @@ -56,25 +56,25 @@ set_a_bus_req(struct device *dev, struct device_attribute *attr,
> if (count > 2)
> return -1;
>
> - mutex_lock(&ci->fsm.lock);
> + mutex_lock(&ci->otg.fsm.lock);
> if (buf[0] == '0') {
> - ci->fsm.a_bus_req = 0;
> + ci->otg.fsm.a_bus_req = 0;
> } else if (buf[0] == '1') {
> /* If a_bus_drop is TRUE, a_bus_req can't be set */
> - if (ci->fsm.a_bus_drop) {
> - mutex_unlock(&ci->fsm.lock);
> + if (ci->otg.fsm.a_bus_drop) {
> + mutex_unlock(&ci->otg.fsm.lock);
> return count;
> }
> - ci->fsm.a_bus_req = 1;
> - if (ci->fsm.otg->state == OTG_STATE_A_PERIPHERAL) {
> + ci->otg.fsm.a_bus_req = 1;
> + if (ci->otg.state == OTG_STATE_A_PERIPHERAL) {
> ci->gadget.host_request_flag = 1;
> - mutex_unlock(&ci->fsm.lock);
> + mutex_unlock(&ci->otg.fsm.lock);
> return count;
> }
> }
>
> ci_otg_queue_work(ci);
> - mutex_unlock(&ci->fsm.lock);
> + mutex_unlock(&ci->otg.fsm.lock);
>
> return count;
> }
> @@ -89,7 +89,7 @@ get_a_bus_drop(struct device *dev, struct device_attribute *attr, char *buf)
>
> next = buf;
> size = PAGE_SIZE;
> - t = scnprintf(next, size, "%d\n", ci->fsm.a_bus_drop);
> + t = scnprintf(next, size, "%d\n", ci->otg.fsm.a_bus_drop);
> size -= t;
> next += t;
>
> @@ -105,16 +105,16 @@ set_a_bus_drop(struct device *dev, struct device_attribute *attr,
> if (count > 2)
> return -1;
>
> - mutex_lock(&ci->fsm.lock);
> + mutex_lock(&ci->otg.fsm.lock);
> if (buf[0] == '0') {
> - ci->fsm.a_bus_drop = 0;
> + ci->otg.fsm.a_bus_drop = 0;
> } else if (buf[0] == '1') {
> - ci->fsm.a_bus_drop = 1;
> - ci->fsm.a_bus_req = 0;
> + ci->otg.fsm.a_bus_drop = 1;
> + ci->otg.fsm.a_bus_req = 0;
> }
>
> ci_otg_queue_work(ci);
> - mutex_unlock(&ci->fsm.lock);
> + mutex_unlock(&ci->otg.fsm.lock);
>
> return count;
> }
> @@ -130,7 +130,7 @@ get_b_bus_req(struct device *dev, struct device_attribute *attr, char *buf)
>
> next = buf;
> size = PAGE_SIZE;
> - t = scnprintf(next, size, "%d\n", ci->fsm.b_bus_req);
> + t = scnprintf(next, size, "%d\n", ci->otg.fsm.b_bus_req);
> size -= t;
> next += t;
>
> @@ -146,20 +146,20 @@ set_b_bus_req(struct device *dev, struct device_attribute *attr,
> if (count > 2)
> return -1;
>
> - mutex_lock(&ci->fsm.lock);
> + mutex_lock(&ci->otg.fsm.lock);
> if (buf[0] == '0')
> - ci->fsm.b_bus_req = 0;
> + ci->otg.fsm.b_bus_req = 0;
> else if (buf[0] == '1') {
> - ci->fsm.b_bus_req = 1;
> - if (ci->fsm.otg->state == OTG_STATE_B_PERIPHERAL) {
> + ci->otg.fsm.b_bus_req = 1;
> + if (ci->otg.state == OTG_STATE_B_PERIPHERAL) {
> ci->gadget.host_request_flag = 1;
> - mutex_unlock(&ci->fsm.lock);
> + mutex_unlock(&ci->otg.fsm.lock);
> return count;
> }
> }
>
> ci_otg_queue_work(ci);
> - mutex_unlock(&ci->fsm.lock);
> + mutex_unlock(&ci->otg.fsm.lock);
>
> return count;
> }
> @@ -174,12 +174,12 @@ set_a_clr_err(struct device *dev, struct device_attribute *attr,
> if (count > 2)
> return -1;
>
> - mutex_lock(&ci->fsm.lock);
> + mutex_lock(&ci->otg.fsm.lock);
> if (buf[0] == '1')
> - ci->fsm.a_clr_err = 1;
> + ci->otg.fsm.a_clr_err = 1;
>
> ci_otg_queue_work(ci);
> - mutex_unlock(&ci->fsm.lock);
> + mutex_unlock(&ci->otg.fsm.lock);
>
> return count;
> }
> @@ -287,64 +287,64 @@ static void ci_otg_del_timer(struct ci_hdrc *ci, enum otg_fsm_timer t)
> /* OTG FSM timer handlers */
> static int a_wait_vrise_tmout(struct ci_hdrc *ci)
> {
> - ci->fsm.a_wait_vrise_tmout = 1;
> + ci->otg.fsm.a_wait_vrise_tmout = 1;
> return 0;
> }
>
> static int a_wait_vfall_tmout(struct ci_hdrc *ci)
> {
> - ci->fsm.a_wait_vfall_tmout = 1;
> + ci->otg.fsm.a_wait_vfall_tmout = 1;
> return 0;
> }
>
> static int a_wait_bcon_tmout(struct ci_hdrc *ci)
> {
> - ci->fsm.a_wait_bcon_tmout = 1;
> + ci->otg.fsm.a_wait_bcon_tmout = 1;
> return 0;
> }
>
> static int a_aidl_bdis_tmout(struct ci_hdrc *ci)
> {
> - ci->fsm.a_aidl_bdis_tmout = 1;
> + ci->otg.fsm.a_aidl_bdis_tmout = 1;
> return 0;
> }
>
> static int b_ase0_brst_tmout(struct ci_hdrc *ci)
> {
> - ci->fsm.b_ase0_brst_tmout = 1;
> + ci->otg.fsm.b_ase0_brst_tmout = 1;
> return 0;
> }
>
> static int a_bidl_adis_tmout(struct ci_hdrc *ci)
> {
> - ci->fsm.a_bidl_adis_tmout = 1;
> + ci->otg.fsm.a_bidl_adis_tmout = 1;
> return 0;
> }
>
> static int b_aidl_bdis_tmout(struct ci_hdrc *ci)
> {
> - ci->fsm.a_bus_suspend = 1;
> + ci->otg.fsm.a_bus_suspend = 1;
> return 0;
> }
>
> static int b_se0_srp_tmout(struct ci_hdrc *ci)
> {
> - ci->fsm.b_se0_srp = 1;
> + ci->otg.fsm.b_se0_srp = 1;
> return 0;
> }
>
> static int b_srp_fail_tmout(struct ci_hdrc *ci)
> {
> - ci->fsm.b_srp_done = 1;
> + ci->otg.fsm.b_srp_done = 1;
> return 1;
> }
>
> static int b_data_pls_tmout(struct ci_hdrc *ci)
> {
> - ci->fsm.b_srp_done = 1;
> - ci->fsm.b_bus_req = 0;
> - if (ci->fsm.power_up)
> - ci->fsm.power_up = 0;
> + ci->otg.fsm.b_srp_done = 1;
> + ci->otg.fsm.b_bus_req = 0;
> + if (ci->otg.fsm.power_up)
> + ci->otg.fsm.power_up = 0;
> hw_write_otgsc(ci, OTGSC_HABA, 0);
> pm_runtime_put(ci->dev);
> return 0;
> @@ -352,9 +352,9 @@ static int b_data_pls_tmout(struct ci_hdrc *ci)
>
> static int b_ssend_srp_tmout(struct ci_hdrc *ci)
> {
> - ci->fsm.b_ssend_srp = 1;
> + ci->otg.fsm.b_ssend_srp = 1;
> /* only vbus fall below B_sess_vld in b_idle state */
> - if (ci->fsm.otg->state == OTG_STATE_B_IDLE)
> + if (ci->otg.state == OTG_STATE_B_IDLE)
> return 0;
> else
> return 1;
> @@ -435,18 +435,18 @@ static int ci_otg_init_timers(struct ci_hdrc *ci)
> /* -------------------------------------------------------------*/
> /* Operations that will be called from OTG Finite State Machine */
> /* -------------------------------------------------------------*/
> -static void ci_otg_fsm_add_timer(struct otg_fsm *fsm, enum otg_fsm_timer t)
> +static void ci_otg_fsm_add_timer(struct usb_otg *otg, enum otg_fsm_timer t)
> {
> - struct ci_hdrc *ci = container_of(fsm, struct ci_hdrc, fsm);
> + struct ci_hdrc *ci = container_of(otg, struct ci_hdrc, otg);
>
> if (t < NUM_OTG_FSM_TIMERS)
> ci_otg_add_timer(ci, t);
> return;
> }
>
> -static void ci_otg_fsm_del_timer(struct otg_fsm *fsm, enum otg_fsm_timer t)
> +static void ci_otg_fsm_del_timer(struct usb_otg *otg, enum otg_fsm_timer t)
> {
> - struct ci_hdrc *ci = container_of(fsm, struct ci_hdrc, fsm);
> + struct ci_hdrc *ci = container_of(otg, struct ci_hdrc, otg);
>
> if (t < NUM_OTG_FSM_TIMERS)
> ci_otg_del_timer(ci, t);
> @@ -457,10 +457,10 @@ static void ci_otg_fsm_del_timer(struct otg_fsm *fsm, enum otg_fsm_timer t)
> * A-device drive vbus: turn on vbus regulator and enable port power
> * Data pulse irq should be disabled while vbus is on.
> */
> -static void ci_otg_drv_vbus(struct otg_fsm *fsm, int on)
> +static void ci_otg_drv_vbus(struct usb_otg *otg, int on)
> {
> int ret;
> - struct ci_hdrc *ci = container_of(fsm, struct ci_hdrc, fsm);
> + struct ci_hdrc *ci = container_of(otg, struct ci_hdrc, otg);
>
> if (on) {
> /* Enable power power */
> @@ -478,23 +478,23 @@ static void ci_otg_drv_vbus(struct otg_fsm *fsm, int on)
> /* Disable data pulse irq */
> hw_write_otgsc(ci, OTGSC_DPIE, 0);
>
> - fsm->a_srp_det = 0;
> - fsm->power_up = 0;
> + otg->fsm.a_srp_det = 0;
> + otg->fsm.power_up = 0;
> } else {
> if (ci->platdata->reg_vbus)
> regulator_disable(ci->platdata->reg_vbus);
>
> - fsm->a_bus_drop = 1;
> - fsm->a_bus_req = 0;
> + otg->fsm.a_bus_drop = 1;
> + otg->fsm.a_bus_req = 0;
> }
> }
>
> /*
> * Control data line by Run Stop bit.
> */
> -static void ci_otg_loc_conn(struct otg_fsm *fsm, int on)
> +static void ci_otg_loc_conn(struct usb_otg *otg, int on)
> {
> - struct ci_hdrc *ci = container_of(fsm, struct ci_hdrc, fsm);
> + struct ci_hdrc *ci = container_of(otg, struct ci_hdrc, otg);
>
> if (on)
> hw_write(ci, OP_USBCMD, USBCMD_RS, USBCMD_RS);
> @@ -511,14 +511,14 @@ static void ci_otg_loc_conn(struct otg_fsm *fsm, int on)
> * so the usb device class driver need support autosuspend,
> * otherwise the bus suspend will not happen.
> */
> -static void ci_otg_loc_sof(struct otg_fsm *fsm, int on)
> +static void ci_otg_loc_sof(struct usb_otg *otg, int on)
> {
> struct usb_device *udev;
>
> - if (!fsm->otg->host)
> + if (!otg->host)
> return;
>
> - udev = usb_hub_find_child(fsm->otg->host->root_hub, 1);
> + udev = usb_hub_find_child(otg->host->root_hub, 1);
> if (!udev)
> return;
>
> @@ -534,9 +534,9 @@ static void ci_otg_loc_sof(struct otg_fsm *fsm, int on)
> * Start SRP pulsing by data-line pulsing,
> * no v-bus pulsing followed
> */
> -static void ci_otg_start_pulse(struct otg_fsm *fsm)
> +static void ci_otg_start_pulse(struct usb_otg *otg)
> {
> - struct ci_hdrc *ci = container_of(fsm, struct ci_hdrc, fsm);
> + struct ci_hdrc *ci = container_of(otg, struct ci_hdrc, otg);
>
> /* Hardware Assistant Data pulse */
> hw_write_otgsc(ci, OTGSC_HADP, OTGSC_HADP);
> @@ -545,9 +545,9 @@ static void ci_otg_start_pulse(struct otg_fsm *fsm)
> ci_otg_add_timer(ci, B_DATA_PLS);
> }
>
> -static int ci_otg_start_host(struct otg_fsm *fsm, int on)
> +static int ci_otg_start_host(struct usb_otg *otg, int on)
> {
> - struct ci_hdrc *ci = container_of(fsm, struct ci_hdrc, fsm);
> + struct ci_hdrc *ci = container_of(otg, struct ci_hdrc, otg);
>
> if (on) {
> ci_role_stop(ci);
> @@ -559,9 +559,9 @@ static int ci_otg_start_host(struct otg_fsm *fsm, int on)
> return 0;
> }
>
> -static int ci_otg_start_gadget(struct otg_fsm *fsm, int on)
> +static int ci_otg_start_gadget(struct usb_otg *otg, int on)
> {
> - struct ci_hdrc *ci = container_of(fsm, struct ci_hdrc, fsm);
> + struct ci_hdrc *ci = container_of(otg, struct ci_hdrc, otg);
>
> if (on)
> usb_gadget_vbus_connect(&ci->gadget);
> @@ -588,13 +588,13 @@ int ci_otg_fsm_work(struct ci_hdrc *ci)
> * Don't do fsm transition for B device
> * when there is no gadget class driver
> */
> - if (ci->fsm.id && !(ci->driver) &&
> - ci->fsm.otg->state < OTG_STATE_A_IDLE)
> + if (ci->otg.fsm.id && !(ci->driver) &&
> + ci->otg.state < OTG_STATE_A_IDLE)
> return 0;
>
> pm_runtime_get_sync(ci->dev);
> - if (otg_statemachine(&ci->fsm)) {
> - if (ci->fsm.otg->state == OTG_STATE_A_IDLE) {
> + if (otg_statemachine(&ci->otg)) {
> + if (ci->otg.state == OTG_STATE_A_IDLE) {
> /*
> * Further state change for cases:
> * a_idle to b_idle; or
> @@ -603,8 +603,8 @@ int ci_otg_fsm_work(struct ci_hdrc *ci)
> * consequently; or
> * a_idle to a_wait_vrise when power up
> */
> - if ((ci->fsm.id) || (ci->id_event) ||
> - (ci->fsm.power_up)) {
> + if ((ci->otg.fsm.id) || (ci->id_event) ||
> + (ci->otg.fsm.power_up)) {
> ci_otg_queue_work(ci);
> } else {
> /* Enable data pulse irq */
> @@ -615,16 +615,16 @@ int ci_otg_fsm_work(struct ci_hdrc *ci)
> }
> if (ci->id_event)
> ci->id_event = false;
> - } else if (ci->fsm.otg->state == OTG_STATE_B_IDLE) {
> - if (ci->fsm.b_sess_vld) {
> - ci->fsm.power_up = 0;
> + } else if (ci->otg.state == OTG_STATE_B_IDLE) {
> + if (ci->otg.fsm.b_sess_vld) {
> + ci->otg.fsm.power_up = 0;
> /*
> * Further transite to b_periphearl state
> * when register gadget driver with vbus on
> */
> ci_otg_queue_work(ci);
> }
> - } else if (ci->fsm.otg->state == OTG_STATE_A_HOST) {
> + } else if (ci->otg.state == OTG_STATE_A_HOST) {
> pm_runtime_mark_last_busy(ci->dev);
> pm_runtime_put_autosuspend(ci->dev);
> return 0;
> @@ -641,13 +641,13 @@ int ci_otg_fsm_work(struct ci_hdrc *ci)
> static void ci_otg_fsm_event(struct ci_hdrc *ci)
> {
> u32 intr_sts, otg_bsess_vld, port_conn;
> - struct otg_fsm *fsm = &ci->fsm;
> + struct otg_fsm *fsm = &ci->otg.fsm;
>
> intr_sts = hw_read_intr_status(ci);
> otg_bsess_vld = hw_read_otgsc(ci, OTGSC_BSV);
> port_conn = hw_read(ci, OP_PORTSC, PORTSC_CCS);
>
> - switch (ci->fsm.otg->state) {
> + switch (ci->otg.state) {
> case OTG_STATE_A_WAIT_BCON:
> if (port_conn) {
> fsm->b_conn = 1;
> @@ -737,7 +737,7 @@ irqreturn_t ci_otg_fsm_irq(struct ci_hdrc *ci)
> {
> irqreturn_t retval = IRQ_NONE;
> u32 otgsc, otg_int_src = 0;
> - struct otg_fsm *fsm = &ci->fsm;
> + struct otg_fsm *fsm = &ci->otg.fsm;
>
> otgsc = hw_read_otgsc(ci, ~0);
> otg_int_src = otgsc & OTGSC_INT_STATUS_BITS & (otgsc >> 8);
> @@ -800,18 +800,17 @@ int ci_hdrc_otg_fsm_init(struct ci_hdrc *ci)
> ci->otg.usb_phy = ci->usb_phy;
>
> ci->otg.gadget = &ci->gadget;
> - ci->fsm.otg = &ci->otg;
> - ci->fsm.power_up = 1;
> - ci->fsm.id = hw_read_otgsc(ci, OTGSC_ID) ? 1 : 0;
> - ci->fsm.otg->state = OTG_STATE_UNDEFINED;
> - ci->fsm.ops = &ci_otg_ops;
> - ci->fsm.dev = ci->dev;
> + ci->otg.fsm.power_up = 1;
> + ci->otg.fsm.id = hw_read_otgsc(ci, OTGSC_ID) ? 1 : 0;
> + ci->otg.state = OTG_STATE_UNDEFINED;
> + ci->otg.fsm.ops = &ci_otg_ops;
> + ci->otg.fsm.dev = ci->dev;
> ci->gadget.hnp_polling_support = 1;
> - ci->fsm.host_req_flag = devm_kzalloc(ci->dev, 1, GFP_KERNEL);
> - if (!ci->fsm.host_req_flag)
> + ci->otg.fsm.host_req_flag = devm_kzalloc(ci->dev, 1, GFP_KERNEL);
> + if (!ci->otg.fsm.host_req_flag)
> return -ENOMEM;
>
> - mutex_init(&ci->fsm.lock);
> + mutex_init(&ci->otg.fsm.lock);
>
> retval = ci_otg_init_timers(ci);
> if (retval) {
> @@ -831,10 +830,10 @@ int ci_hdrc_otg_fsm_init(struct ci_hdrc *ci)
> /* Enable A vbus valid irq */
> hw_write_otgsc(ci, OTGSC_AVVIE, OTGSC_AVVIE);
>
> - if (ci->fsm.id) {
> - ci->fsm.b_ssend_srp =
> + if (ci->otg.fsm.id) {
> + ci->otg.fsm.b_ssend_srp =
> hw_read_otgsc(ci, OTGSC_BSV) ? 0 : 1;
> - ci->fsm.b_sess_vld =
> + ci->otg.fsm.b_sess_vld =
> hw_read_otgsc(ci, OTGSC_BSV) ? 1 : 0;
> /* Enable BSV irq */
> hw_write_otgsc(ci, OTGSC_BSVIE, OTGSC_BSVIE);
> diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c
> index 065f5d9..7a6b0b5 100644
> --- a/drivers/usb/chipidea/udc.c
> +++ b/drivers/usb/chipidea/udc.c
> @@ -20,6 +20,7 @@
> #include <linux/pm_runtime.h>
> #include <linux/usb/ch9.h>
> #include <linux/usb/gadget.h>
> +#include <linux/usb/otg.h>
> #include <linux/usb/otg-fsm.h>
> #include <linux/usb/chipidea.h>
>
> @@ -1739,7 +1740,7 @@ static int ci_udc_start(struct usb_gadget *gadget,
> ci->driver = driver;
>
> /* Start otg fsm for B-device */
> - if (ci_otg_is_fsm_mode(ci) && ci->fsm.id) {
> + if (ci_otg_is_fsm_mode(ci) && ci->otg.fsm.id) {
> ci_hdrc_otg_fsm_start(ci);
> return retval;
> }
> @@ -1767,15 +1768,15 @@ static void ci_udc_stop_for_otg_fsm(struct ci_hdrc *ci)
> if (!ci_otg_is_fsm_mode(ci))
> return;
>
> - mutex_lock(&ci->fsm.lock);
> - if (ci->fsm.otg->state == OTG_STATE_A_PERIPHERAL) {
> - ci->fsm.a_bidl_adis_tmout = 1;
> + mutex_lock(&ci->otg.fsm.lock);
> + if (ci->otg.state == OTG_STATE_A_PERIPHERAL) {
> + ci->otg.fsm.a_bidl_adis_tmout = 1;
> ci_hdrc_otg_fsm_start(ci);
> - } else if (ci->fsm.otg->state == OTG_STATE_B_PERIPHERAL) {
> - ci->fsm.protocol = PROTO_UNDEF;
> - ci->fsm.otg->state = OTG_STATE_UNDEFINED;
> + } else if (ci->otg.state == OTG_STATE_B_PERIPHERAL) {
> + ci->otg.fsm.protocol = PROTO_UNDEF;
> + ci->otg.state = OTG_STATE_UNDEFINED;
> }
> - mutex_unlock(&ci->fsm.lock);
> + mutex_unlock(&ci->otg.fsm.lock);
> }
>
> /**
> diff --git a/drivers/usb/common/usb-otg-fsm.c b/drivers/usb/common/usb-otg-fsm.c
> index c5a61fe..abc462c 100644
> --- a/drivers/usb/common/usb-otg-fsm.c
> +++ b/drivers/usb/common/usb-otg-fsm.c
> @@ -33,6 +33,7 @@
> /* Change USB protocol when there is a protocol change */
> static int otg_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) {
> @@ -41,17 +42,17 @@ static int otg_set_protocol(struct otg_fsm *fsm, int protocol)
> fsm->protocol, protocol);
> /* stop old protocol */
> if (fsm->protocol == PROTO_HOST)
> - ret = otg_start_host(fsm, 0);
> + ret = otg_start_host(otg, 0);
> else if (fsm->protocol == PROTO_GADGET)
> - ret = otg_start_gadget(fsm, 0);
> + ret = otg_start_gadget(otg, 0);
> if (ret)
> return ret;
>
> /* start new protocol */
> if (protocol == PROTO_HOST)
> - ret = otg_start_host(fsm, 1);
> + ret = otg_start_host(otg, 1);
> else if (protocol == PROTO_GADGET)
> - ret = otg_start_gadget(fsm, 1);
> + ret = otg_start_gadget(otg, 1);
> if (ret)
> return ret;
>
> @@ -65,9 +66,11 @@ static int otg_set_protocol(struct otg_fsm *fsm, int protocol)
> /* 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)
> {
> + struct usb_otg *otg = container_of(fsm, struct usb_otg, fsm);
> +
> switch (old_state) {
> case OTG_STATE_B_IDLE:
> - otg_del_timer(fsm, B_SE0_SRP);
> + otg_del_timer(otg, B_SE0_SRP);
> fsm->b_se0_srp = 0;
> fsm->adp_sns = 0;
> fsm->adp_prb = 0;
> @@ -77,11 +80,11 @@ static void otg_leave_state(struct otg_fsm *fsm, enum usb_otg_state old_state)
> fsm->b_srp_done = 0;
> break;
> case OTG_STATE_B_PERIPHERAL:
> - if (fsm->otg->gadget)
> - fsm->otg->gadget->host_request_flag = 0;
> + if (otg->gadget)
> + otg->gadget->host_request_flag = 0;
> break;
> case OTG_STATE_B_WAIT_ACON:
> - otg_del_timer(fsm, B_ASE0_BRST);
> + otg_del_timer(otg, B_ASE0_BRST);
> fsm->b_ase0_brst_tmout = 0;
> break;
> case OTG_STATE_B_HOST:
> @@ -90,31 +93,31 @@ static void otg_leave_state(struct otg_fsm *fsm, enum usb_otg_state old_state)
> fsm->adp_prb = 0;
> break;
> case OTG_STATE_A_WAIT_VRISE:
> - otg_del_timer(fsm, A_WAIT_VRISE);
> + otg_del_timer(otg, A_WAIT_VRISE);
> fsm->a_wait_vrise_tmout = 0;
> break;
> case OTG_STATE_A_WAIT_BCON:
> - otg_del_timer(fsm, A_WAIT_BCON);
> + otg_del_timer(otg, A_WAIT_BCON);
> fsm->a_wait_bcon_tmout = 0;
> break;
> case OTG_STATE_A_HOST:
> - otg_del_timer(fsm, A_WAIT_ENUM);
> + otg_del_timer(otg, A_WAIT_ENUM);
> break;
> case OTG_STATE_A_SUSPEND:
> - otg_del_timer(fsm, A_AIDL_BDIS);
> + otg_del_timer(otg, A_AIDL_BDIS);
> fsm->a_aidl_bdis_tmout = 0;
> fsm->a_suspend_req_inf = 0;
> break;
> case OTG_STATE_A_PERIPHERAL:
> - otg_del_timer(fsm, A_BIDL_ADIS);
> + otg_del_timer(otg, A_BIDL_ADIS);
> fsm->a_bidl_adis_tmout = 0;
> - if (fsm->otg->gadget)
> - fsm->otg->gadget->host_request_flag = 0;
> + if (otg->gadget)
> + otg->gadget->host_request_flag = 0;
> break;
> case OTG_STATE_A_WAIT_VFALL:
> - otg_del_timer(fsm, A_WAIT_VFALL);
> + otg_del_timer(otg, A_WAIT_VFALL);
> fsm->a_wait_vfall_tmout = 0;
> - otg_del_timer(fsm, A_WAIT_VRISE);
> + otg_del_timer(otg, A_WAIT_VRISE);
> break;
> case OTG_STATE_A_VBUS_ERR:
> break;
> @@ -127,17 +130,18 @@ static void otg_hnp_polling_work(struct work_struct *work)
> {
> struct otg_fsm *fsm = container_of(to_delayed_work(work),
> struct otg_fsm, hnp_polling_work);
> + struct usb_otg *otg = container_of(fsm, struct usb_otg, fsm);
> struct usb_device *udev;
> - enum usb_otg_state state = fsm->otg->state;
> + enum usb_otg_state state = otg->state;
> u8 flag;
> int retval;
>
> if (state != OTG_STATE_A_HOST && state != OTG_STATE_B_HOST)
> return;
>
> - udev = usb_hub_find_child(fsm->otg->host->root_hub, 1);
> + udev = usb_hub_find_child(otg->host->root_hub, 1);
> if (!udev) {
> - dev_err(fsm->otg->host->controller,
> + dev_err(otg->host->controller,
> "no usb dev connected, can't start HNP polling\n");
> return;
> }
> @@ -172,7 +176,7 @@ static void otg_hnp_polling_work(struct work_struct *work)
> /* Host request flag is set */
> if (state == OTG_STATE_A_HOST) {
> /* Set b_hnp_enable */
> - if (!fsm->otg->host->b_hnp_enable) {
> + if (!otg->host->b_hnp_enable) {
> retval = usb_control_msg(udev,
> usb_sndctrlpipe(udev, 0),
> USB_REQ_SET_FEATURE, 0,
> @@ -180,14 +184,14 @@ static void otg_hnp_polling_work(struct work_struct *work)
> 0, NULL, 0,
> USB_CTRL_SET_TIMEOUT);
> if (retval >= 0)
> - fsm->otg->host->b_hnp_enable = 1;
> + otg->host->b_hnp_enable = 1;
> }
> fsm->a_bus_req = 0;
> } else if (state == OTG_STATE_B_HOST) {
> fsm->b_bus_req = 0;
> }
>
> - otg_statemachine(fsm);
> + otg_statemachine(otg);
> }
>
> static void otg_start_hnp_polling(struct otg_fsm *fsm)
> @@ -207,133 +211,135 @@ static void otg_start_hnp_polling(struct otg_fsm *fsm)
> /* Called when entering a state */
> static int otg_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state)
> {
> - if (fsm->otg->state == new_state)
> + struct usb_otg *otg = container_of(fsm, struct usb_otg, fsm);
> +
> + if (otg->state == new_state)
> return 0;
> dev_vdbg(fsm->dev, "Set state: %s\n", usb_otg_state_string(new_state));
> - otg_leave_state(fsm, fsm->otg->state);
> + otg_leave_state(fsm, otg->state);
> switch (new_state) {
> case OTG_STATE_B_IDLE:
> - otg_drv_vbus(fsm, 0);
> - otg_chrg_vbus(fsm, 0);
> - otg_loc_conn(fsm, 0);
> - otg_loc_sof(fsm, 0);
> + otg_drv_vbus(otg, 0);
> + otg_chrg_vbus(otg, 0);
> + otg_loc_conn(otg, 0);
> + otg_loc_sof(otg, 0);
> /*
> * Driver is responsible for starting ADP probing
> * if ADP sensing times out.
> */
> - otg_start_adp_sns(fsm);
> + otg_start_adp_sns(otg);
> otg_set_protocol(fsm, PROTO_UNDEF);
> - otg_add_timer(fsm, B_SE0_SRP);
> + otg_add_timer(otg, B_SE0_SRP);
> break;
> case OTG_STATE_B_SRP_INIT:
> - otg_start_pulse(fsm);
> - otg_loc_sof(fsm, 0);
> + otg_start_pulse(otg);
> + otg_loc_sof(otg, 0);
> otg_set_protocol(fsm, PROTO_UNDEF);
> - otg_add_timer(fsm, B_SRP_FAIL);
> + otg_add_timer(otg, B_SRP_FAIL);
> break;
> case OTG_STATE_B_PERIPHERAL:
> - otg_chrg_vbus(fsm, 0);
> - otg_loc_sof(fsm, 0);
> + otg_chrg_vbus(otg, 0);
> + otg_loc_sof(otg, 0);
> otg_set_protocol(fsm, PROTO_GADGET);
> - otg_loc_conn(fsm, 1);
> + otg_loc_conn(otg, 1);
> break;
> case OTG_STATE_B_WAIT_ACON:
> - otg_chrg_vbus(fsm, 0);
> - otg_loc_conn(fsm, 0);
> - otg_loc_sof(fsm, 0);
> + otg_chrg_vbus(otg, 0);
> + otg_loc_conn(otg, 0);
> + otg_loc_sof(otg, 0);
> otg_set_protocol(fsm, PROTO_HOST);
> - otg_add_timer(fsm, B_ASE0_BRST);
> + otg_add_timer(otg, B_ASE0_BRST);
> fsm->a_bus_suspend = 0;
> break;
> case OTG_STATE_B_HOST:
> - otg_chrg_vbus(fsm, 0);
> - otg_loc_conn(fsm, 0);
> - otg_loc_sof(fsm, 1);
> + otg_chrg_vbus(otg, 0);
> + otg_loc_conn(otg, 0);
> + otg_loc_sof(otg, 1);
> otg_set_protocol(fsm, PROTO_HOST);
> - usb_bus_start_enum(fsm->otg->host,
> - fsm->otg->host->otg_port);
> + usb_bus_start_enum(otg->host, otg->host->otg_port);
> otg_start_hnp_polling(fsm);
> break;
> case OTG_STATE_A_IDLE:
> - otg_drv_vbus(fsm, 0);
> - otg_chrg_vbus(fsm, 0);
> - otg_loc_conn(fsm, 0);
> - otg_loc_sof(fsm, 0);
> - otg_start_adp_prb(fsm);
> + otg_drv_vbus(otg, 0);
> + otg_chrg_vbus(otg, 0);
> + otg_loc_conn(otg, 0);
> + otg_loc_sof(otg, 0);
> + otg_start_adp_prb(otg);
> otg_set_protocol(fsm, PROTO_HOST);
> break;
> case OTG_STATE_A_WAIT_VRISE:
> - otg_drv_vbus(fsm, 1);
> - otg_loc_conn(fsm, 0);
> - otg_loc_sof(fsm, 0);
> + otg_drv_vbus(otg, 1);
> + otg_loc_conn(otg, 0);
> + otg_loc_sof(otg, 0);
> otg_set_protocol(fsm, PROTO_HOST);
> - otg_add_timer(fsm, A_WAIT_VRISE);
> + otg_add_timer(otg, A_WAIT_VRISE);
> break;
> case OTG_STATE_A_WAIT_BCON:
> - otg_drv_vbus(fsm, 1);
> - otg_loc_conn(fsm, 0);
> - otg_loc_sof(fsm, 0);
> + otg_drv_vbus(otg, 1);
> + otg_loc_conn(otg, 0);
> + otg_loc_sof(otg, 0);
> otg_set_protocol(fsm, PROTO_HOST);
> - otg_add_timer(fsm, A_WAIT_BCON);
> + otg_add_timer(otg, A_WAIT_BCON);
> break;
> case OTG_STATE_A_HOST:
> - otg_drv_vbus(fsm, 1);
> - otg_loc_conn(fsm, 0);
> - otg_loc_sof(fsm, 1);
> + otg_drv_vbus(otg, 1);
> + otg_loc_conn(otg, 0);
> + otg_loc_sof(otg, 1);
> otg_set_protocol(fsm, PROTO_HOST);
> /*
> * When HNP is triggered while a_bus_req = 0, a_host will
> * suspend too fast to complete a_set_b_hnp_en
> */
> if (!fsm->a_bus_req || fsm->a_suspend_req_inf)
> - otg_add_timer(fsm, A_WAIT_ENUM);
> + otg_add_timer(otg, A_WAIT_ENUM);
> otg_start_hnp_polling(fsm);
> break;
> case OTG_STATE_A_SUSPEND:
> - otg_drv_vbus(fsm, 1);
> - otg_loc_conn(fsm, 0);
> - otg_loc_sof(fsm, 0);
> + otg_drv_vbus(otg, 1);
> + otg_loc_conn(otg, 0);
> + otg_loc_sof(otg, 0);
> otg_set_protocol(fsm, PROTO_HOST);
> - otg_add_timer(fsm, A_AIDL_BDIS);
> + otg_add_timer(otg, A_AIDL_BDIS);
>
> break;
> case OTG_STATE_A_PERIPHERAL:
> - otg_loc_sof(fsm, 0);
> + otg_loc_sof(otg, 0);
> otg_set_protocol(fsm, PROTO_GADGET);
> - otg_drv_vbus(fsm, 1);
> - otg_loc_conn(fsm, 1);
> - otg_add_timer(fsm, A_BIDL_ADIS);
> + otg_drv_vbus(otg, 1);
> + otg_loc_conn(otg, 1);
> + otg_add_timer(otg, A_BIDL_ADIS);
> break;
> case OTG_STATE_A_WAIT_VFALL:
> - otg_drv_vbus(fsm, 0);
> - otg_loc_conn(fsm, 0);
> - otg_loc_sof(fsm, 0);
> + otg_drv_vbus(otg, 0);
> + otg_loc_conn(otg, 0);
> + otg_loc_sof(otg, 0);
> otg_set_protocol(fsm, PROTO_HOST);
> - otg_add_timer(fsm, A_WAIT_VFALL);
> + otg_add_timer(otg, A_WAIT_VFALL);
> break;
> case OTG_STATE_A_VBUS_ERR:
> - otg_drv_vbus(fsm, 0);
> - otg_loc_conn(fsm, 0);
> - otg_loc_sof(fsm, 0);
> + otg_drv_vbus(otg, 0);
> + otg_loc_conn(otg, 0);
> + otg_loc_sof(otg, 0);
> otg_set_protocol(fsm, PROTO_UNDEF);
> break;
> default:
> break;
> }
>
> - fsm->otg->state = new_state;
> + otg->state = new_state;
> fsm->state_changed = 1;
> return 0;
> }
>
> /* State change judgement */
> -int otg_statemachine(struct otg_fsm *fsm)
> +int otg_statemachine(struct usb_otg *otg)
> {
> enum usb_otg_state state;
> + struct otg_fsm *fsm = &otg->fsm;
>
> mutex_lock(&fsm->lock);
>
> - state = fsm->otg->state;
> + state = otg->state;
> fsm->state_changed = 0;
> /* State machine state change judgement */
>
> @@ -348,7 +354,7 @@ int otg_statemachine(struct otg_fsm *fsm)
> case OTG_STATE_B_IDLE:
> if (!fsm->id)
> otg_set_state(fsm, OTG_STATE_A_IDLE);
> - else if (fsm->b_sess_vld && fsm->otg->gadget)
> + else if (fsm->b_sess_vld && otg->gadget)
> otg_set_state(fsm, OTG_STATE_B_PERIPHERAL);
> else if ((fsm->b_bus_req || fsm->adp_change || fsm->power_up) &&
> fsm->b_ssend_srp && fsm->b_se0_srp)
> @@ -361,8 +367,8 @@ int otg_statemachine(struct otg_fsm *fsm)
> case OTG_STATE_B_PERIPHERAL:
> if (!fsm->id || !fsm->b_sess_vld)
> otg_set_state(fsm, OTG_STATE_B_IDLE);
> - else if (fsm->b_bus_req && fsm->otg->
> - gadget->b_hnp_enable && fsm->a_bus_suspend)
> + else if (fsm->b_bus_req && otg->gadget->b_hnp_enable
> + && fsm->a_bus_suspend)
> otg_set_state(fsm, OTG_STATE_B_WAIT_ACON);
> break;
> case OTG_STATE_B_WAIT_ACON:
> @@ -407,7 +413,7 @@ int otg_statemachine(struct otg_fsm *fsm)
> if (fsm->id || fsm->a_bus_drop)
> otg_set_state(fsm, OTG_STATE_A_WAIT_VFALL);
> else if ((!fsm->a_bus_req || fsm->a_suspend_req_inf) &&
> - fsm->otg->host->b_hnp_enable)
> + otg->host->b_hnp_enable)
> otg_set_state(fsm, OTG_STATE_A_SUSPEND);
> else if (!fsm->b_conn)
> otg_set_state(fsm, OTG_STATE_A_WAIT_BCON);
> @@ -415,9 +421,9 @@ int otg_statemachine(struct otg_fsm *fsm)
> otg_set_state(fsm, OTG_STATE_A_VBUS_ERR);
> break;
> case OTG_STATE_A_SUSPEND:
> - if (!fsm->b_conn && fsm->otg->host->b_hnp_enable)
> + if (!fsm->b_conn && otg->host->b_hnp_enable)
> otg_set_state(fsm, OTG_STATE_A_PERIPHERAL);
> - else if (!fsm->b_conn && !fsm->otg->host->b_hnp_enable)
> + else if (!fsm->b_conn && !otg->host->b_hnp_enable)
> otg_set_state(fsm, OTG_STATE_A_WAIT_BCON);
> else if (fsm->a_bus_req || fsm->b_bus_resume)
> otg_set_state(fsm, OTG_STATE_A_HOST);
> diff --git a/drivers/usb/phy/phy-fsl-usb.c b/drivers/usb/phy/phy-fsl-usb.c
> index c57ef5c..a18a2ee 100644
> --- a/drivers/usb/phy/phy-fsl-usb.c
> +++ b/drivers/usb/phy/phy-fsl-usb.c
> @@ -127,7 +127,7 @@ int write_ulpi(u8 addr, u8 data)
> /* Operations that will be called from OTG Finite State Machine */
>
> /* Charge vbus for vbus pulsing in SRP */
> -void fsl_otg_chrg_vbus(struct otg_fsm *fsm, int on)
> +void fsl_otg_chrg_vbus(struct usb_otg *otg, int on)
> {
> u32 tmp;
>
> @@ -163,7 +163,7 @@ void fsl_otg_dischrg_vbus(int on)
> }
>
> /* A-device driver vbus, controlled through PP bit in PORTSC */
> -void fsl_otg_drv_vbus(struct otg_fsm *fsm, int on)
> +void fsl_otg_drv_vbus(struct usb_otg *otg, int on)
> {
> u32 tmp;
>
> @@ -181,7 +181,7 @@ void fsl_otg_drv_vbus(struct otg_fsm *fsm, int on)
> * Pull-up D+, signalling connect by periperal. Also used in
> * data-line pulsing in SRP
> */
> -void fsl_otg_loc_conn(struct otg_fsm *fsm, int on)
> +void fsl_otg_loc_conn(struct usb_otg *otg, int on)
> {
> u32 tmp;
>
> @@ -200,7 +200,7 @@ void fsl_otg_loc_conn(struct otg_fsm *fsm, int on)
> * port. In host mode, controller will automatically send SOF.
> * Suspend will block the data on the port.
> */
> -void fsl_otg_loc_sof(struct otg_fsm *fsm, int on)
> +void fsl_otg_loc_sof(struct usb_otg *otg, int on)
> {
> u32 tmp;
>
> @@ -215,7 +215,7 @@ void fsl_otg_loc_sof(struct otg_fsm *fsm, int on)
> }
>
> /* Start SRP pulsing by data-line pulsing, followed with v-bus pulsing. */
> -void fsl_otg_start_pulse(struct otg_fsm *fsm)
> +void fsl_otg_start_pulse(struct usb_otg *otg)
> {
> u32 tmp;
>
> @@ -228,7 +228,7 @@ void fsl_otg_start_pulse(struct otg_fsm *fsm)
> fsl_otg_loc_conn(1);
> #endif
>
> - fsl_otg_add_timer(fsm, b_data_pulse_tmr);
> + fsl_otg_add_timer(&otg->fsm, b_data_pulse_tmr);
> }
>
> void b_data_pulse_end(unsigned long foo)
> @@ -245,14 +245,14 @@ void b_data_pulse_end(unsigned long foo)
> void fsl_otg_pulse_vbus(void)
> {
> srp_wait_done = 0;
> - fsl_otg_chrg_vbus(&fsl_otg_dev->fsm, 1);
> + fsl_otg_chrg_vbus(&fsl_otg_dev->otg, 1);
> /* start the timer to end vbus charge */
> - fsl_otg_add_timer(&fsl_otg_dev->fsm, b_vbus_pulse_tmr);
> + fsl_otg_add_timer(&fsl_otg_dev->otg.fsm, b_vbus_pulse_tmr);
> }
>
> void b_vbus_pulse_end(unsigned long foo)
> {
> - fsl_otg_chrg_vbus(&fsl_otg_dev->fsm, 0);
> + fsl_otg_chrg_vbus(&fsl_otg_dev->otg, 0);
>
> /*
> * As USB3300 using the same a_sess_vld and b_sess_vld voltage
> @@ -260,7 +260,7 @@ void b_vbus_pulse_end(unsigned long foo)
> * residual voltage of vbus pulsing and A device pull up
> */
> fsl_otg_dischrg_vbus(1);
> - fsl_otg_add_timer(&fsl_otg_dev->fsm, b_srp_wait_tmr);
> + fsl_otg_add_timer(&fsl_otg_dev->otg.fsm, b_srp_wait_tmr);
> }
>
> void b_srp_end(unsigned long foo)
> @@ -269,8 +269,8 @@ void b_srp_end(unsigned long foo)
> srp_wait_done = 1;
>
> if ((fsl_otg_dev->phy.otg->state == OTG_STATE_B_SRP_INIT) &&
> - fsl_otg_dev->fsm.b_sess_vld)
> - fsl_otg_dev->fsm.b_srp_done = 1;
> + fsl_otg_dev->otg.fsm.b_sess_vld)
> + fsl_otg_dev->otg.fsm.b_srp_done = 1;
> }
>
> /*
> @@ -282,9 +282,9 @@ void a_wait_enum(unsigned long foo)
> {
> VDBG("a_wait_enum timeout\n");
> if (!fsl_otg_dev->phy.otg->host->b_hnp_enable)
> - fsl_otg_add_timer(&fsl_otg_dev->fsm, a_wait_enum_tmr);
> + fsl_otg_add_timer(&fsl_otg_dev->otg.fsm, a_wait_enum_tmr);
> else
> - otg_statemachine(&fsl_otg_dev->fsm);
> + otg_statemachine(&fsl_otg_dev->otg);
> }
>
> /* The timeout callback function to set time out bit */
> @@ -421,7 +421,7 @@ void fsl_otg_add_timer(struct otg_fsm *fsm, void *gtimer)
> list_add_tail(&timer->list, &active_timers);
> }
>
> -static void fsl_otg_fsm_add_timer(struct otg_fsm *fsm, enum otg_fsm_timer t)
> +static void fsl_otg_fsm_add_timer(struct usb_otg *otg, enum otg_fsm_timer t)
> {
> struct fsl_otg_timer *timer;
>
> @@ -429,7 +429,7 @@ static void fsl_otg_fsm_add_timer(struct otg_fsm *fsm, enum otg_fsm_timer t)
> if (!timer)
> return;
>
> - fsl_otg_add_timer(fsm, timer);
> + fsl_otg_add_timer(&otg->fsm, timer);
> }
>
> /* Remove timer from the timer list; clear timeout status */
> @@ -443,7 +443,7 @@ void fsl_otg_del_timer(struct otg_fsm *fsm, void *gtimer)
> list_del(&timer->list);
> }
>
> -static void fsl_otg_fsm_del_timer(struct otg_fsm *fsm, enum otg_fsm_timer t)
> +static void fsl_otg_fsm_del_timer(struct usb_otg *otg, enum otg_fsm_timer t)
> {
> struct fsl_otg_timer *timer;
>
> @@ -451,7 +451,7 @@ static void fsl_otg_fsm_del_timer(struct otg_fsm *fsm, enum otg_fsm_timer t)
> if (!timer)
> return;
>
> - fsl_otg_del_timer(fsm, timer);
> + fsl_otg_del_timer(&otg->fsm, timer);
> }
>
> /* Reset controller, not reset the bus */
> @@ -467,9 +467,9 @@ void otg_reset_controller(void)
> }
>
> /* Call suspend/resume routines in host driver */
> -int fsl_otg_start_host(struct otg_fsm *fsm, int on)
> +int fsl_otg_start_host(struct usb_otg *otg, int on)
> {
> - struct usb_otg *otg = fsm->otg;
> + struct otg_fsm *fsm = &otg->fsm;
> struct device *dev;
> struct fsl_otg *otg_dev =
> container_of(otg->usb_phy, struct fsl_otg, phy);
> @@ -496,7 +496,7 @@ int fsl_otg_start_host(struct otg_fsm *fsm, int on)
> retval = dev->driver->pm->resume(dev);
> if (fsm->id) {
> /* default-b */
> - fsl_otg_drv_vbus(fsm, 1);
> + fsl_otg_drv_vbus(otg, 1);
> /*
> * Workaround: b_host can't driver
> * vbus, but PP in PORTSC needs to
> @@ -521,7 +521,7 @@ int fsl_otg_start_host(struct otg_fsm *fsm, int on)
> retval = dev->driver->pm->suspend(dev);
> if (fsm->id)
> /* default-b */
> - fsl_otg_drv_vbus(fsm, 0);
> + fsl_otg_drv_vbus(otg, 0);
> }
> otg_dev->host_working = 0;
> }
> @@ -534,9 +534,8 @@ end:
> * Call suspend and resume function in udc driver
> * to stop and start udc driver.
> */
> -int fsl_otg_start_gadget(struct otg_fsm *fsm, int on)
> +int fsl_otg_start_gadget(struct usb_otg *otg, int on)
> {
> - struct usb_otg *otg = fsm->otg;
> struct device *dev;
>
> if (!otg->gadget || !otg->gadget->dev.parent)
> @@ -573,14 +572,14 @@ static int fsl_otg_set_host(struct usb_otg *otg, struct usb_bus *host)
>
> otg->host = host;
>
> - otg_dev->fsm.a_bus_drop = 0;
> - otg_dev->fsm.a_bus_req = 1;
> + otg->fsm.a_bus_drop = 0;
> + otg->fsm.a_bus_req = 1;
>
> if (host) {
> VDBG("host off......\n");
>
> otg->host->otg_port = fsl_otg_initdata.otg_port;
> - otg->host->is_b_host = otg_dev->fsm.id;
> + otg->host->is_b_host = otg->fsm.id;
> /*
> * must leave time for hub_wq to finish its thing
> * before yanking the host driver out from under it,
> @@ -594,7 +593,7 @@ static int fsl_otg_set_host(struct usb_otg *otg, struct usb_bus *host)
> if (!(fsl_readl(&otg_dev->dr_mem_map->otgsc) &
> OTGSC_STS_USB_ID)) {
> /* Mini-A cable connected */
> - struct otg_fsm *fsm = &otg_dev->fsm;
> + struct otg_fsm *fsm = &otg->fsm;
>
> otg->state = OTG_STATE_UNDEFINED;
> fsm->protocol = PROTO_UNDEF;
> @@ -603,7 +602,7 @@ static int fsl_otg_set_host(struct usb_otg *otg, struct usb_bus *host)
>
> otg_dev->host_working = 0;
>
> - otg_statemachine(&otg_dev->fsm);
> + otg_statemachine(otg);
>
> return 0;
> }
> @@ -628,22 +627,22 @@ static int fsl_otg_set_peripheral(struct usb_otg *otg,
> otg->gadget->ops->vbus_draw(otg->gadget, 0);
> usb_gadget_vbus_disconnect(otg->gadget);
> otg->gadget = 0;
> - otg_dev->fsm.b_bus_req = 0;
> - otg_statemachine(&otg_dev->fsm);
> + otg->fsm.b_bus_req = 0;
> + otg_statemachine(otg);
> return 0;
> }
>
> otg->gadget = gadget;
> - otg->gadget->is_a_peripheral = !otg_dev->fsm.id;
> + otg->gadget->is_a_peripheral = !otg->fsm.id;
>
> - otg_dev->fsm.b_bus_req = 1;
> + otg->fsm.b_bus_req = 1;
>
> /* start the gadget right away if the ID pin says Mini-B */
> - pr_debug("ID pin=%d\n", otg_dev->fsm.id);
> - if (otg_dev->fsm.id == 1) {
> - fsl_otg_start_host(&otg_dev->fsm, 0);
> - otg_drv_vbus(&otg_dev->fsm, 0);
> - fsl_otg_start_gadget(&otg_dev->fsm, 1);
> + pr_debug("ID pin=%d\n", otg->fsm.id);
> + if (otg->fsm.id == 1) {
> + fsl_otg_start_host(otg, 0);
> + otg_drv_vbus(otg, 0);
> + fsl_otg_start_gadget(otg, 1);
> }
>
> return 0;
> @@ -672,13 +671,14 @@ static int fsl_otg_set_power(struct usb_phy *phy, unsigned mA)
> */
> static void fsl_otg_event(struct work_struct *work)
> {
> - struct fsl_otg *og = container_of(work, struct fsl_otg, otg_event.work);
> - struct otg_fsm *fsm = &og->fsm;
> -
> - if (fsm->id) { /* switch to gadget */
> - fsl_otg_start_host(fsm, 0);
> - otg_drv_vbus(fsm, 0);
> - fsl_otg_start_gadget(fsm, 1);
> + struct fsl_otg *fotg = container_of(work, struct fsl_otg,
> + otg_event.work);
> + struct usb_otg *otg = &fotg->otg;
> +
> + if (otg->fsm.id) { /* switch to gadget */
> + fsl_otg_start_host(otg, 0);
> + otg_drv_vbus(otg, 0);
> + fsl_otg_start_gadget(otg, 1);
> }
> }
>
> @@ -694,8 +694,8 @@ static int fsl_otg_start_srp(struct usb_otg *otg)
> if (otg_dev != fsl_otg_dev)
> return -ENODEV;
>
> - otg_dev->fsm.b_bus_req = 1;
> - otg_statemachine(&otg_dev->fsm);
> + otg->fsm.b_bus_req = 1;
> + otg_statemachine(otg);
>
> return 0;
> }
> @@ -715,8 +715,8 @@ static int fsl_otg_start_hnp(struct usb_otg *otg)
> pr_debug("start_hnp...\n");
>
> /* clear a_bus_req to enter a_suspend state */
> - otg_dev->fsm.a_bus_req = 0;
> - otg_statemachine(&otg_dev->fsm);
> + otg->fsm.a_bus_req = 0;
> + otg_statemachine(otg);
>
> return 0;
> }
> @@ -729,8 +729,8 @@ static int fsl_otg_start_hnp(struct usb_otg *otg)
> */
> irqreturn_t fsl_otg_isr(int irq, void *dev_id)
> {
> - struct otg_fsm *fsm = &((struct fsl_otg *)dev_id)->fsm;
> - struct usb_otg *otg = ((struct fsl_otg *)dev_id)->phy.otg;
> + struct usb_otg *otg = &((struct fsl_otg *)dev_id)->otg;
> + struct otg_fsm *fsm = &otg->fsm;
> u32 otg_int_src, otg_sc;
>
> otg_sc = fsl_readl(&usb_dr_regs->otgsc);
> @@ -768,9 +768,9 @@ irqreturn_t fsl_otg_isr(int irq, void *dev_id)
> cancel_delayed_work(&
> ((struct fsl_otg *)dev_id)->
> otg_event);
> - fsl_otg_start_gadget(fsm, 0);
> - otg_drv_vbus(fsm, 1);
> - fsl_otg_start_host(fsm, 1);
> + fsl_otg_start_gadget(otg, 0);
> + otg_drv_vbus(otg, 1);
> + fsl_otg_start_host(otg, 1);
> }
> return IRQ_HANDLED;
> }
> @@ -806,25 +806,21 @@ static int fsl_otg_conf(struct platform_device *pdev)
> if (!fsl_otg_tc)
> return -ENOMEM;
>
> - fsl_otg_tc->phy.otg = kzalloc(sizeof(struct usb_otg), GFP_KERNEL);
> - if (!fsl_otg_tc->phy.otg) {
> - kfree(fsl_otg_tc);
> - return -ENOMEM;
> - }
> + fsl_otg_tc->phy.otg = &fsl_otg_tc->otg;
>
> INIT_DELAYED_WORK(&fsl_otg_tc->otg_event, fsl_otg_event);
>
> INIT_LIST_HEAD(&active_timers);
> - status = fsl_otg_init_timers(&fsl_otg_tc->fsm);
> + status = fsl_otg_init_timers(&fsl_otg_tc->otg.fsm);
> if (status) {
> pr_info("Couldn't init OTG timers\n");
> goto err;
> }
> - mutex_init(&fsl_otg_tc->fsm.lock);
> + mutex_init(&fsl_otg_tc->otg.fsm.lock);
>
> /* Set OTG state machine operations */
> - fsl_otg_tc->fsm.ops = &fsl_otg_ops;
> - fsl_otg_tc->fsm.dev = &pdev->dev;
> + fsl_otg_tc->otg.fsm.ops = &fsl_otg_ops;
> + fsl_otg_tc->otg.fsm.dev = &pdev->dev;
>
> /* initialize the otg structure */
> fsl_otg_tc->phy.label = DRIVER_DESC;
> @@ -859,18 +855,15 @@ int usb_otg_start(struct platform_device *pdev)
> {
> struct fsl_otg *p_otg;
> struct usb_phy *otg_trans = usb_get_phy(USB_PHY_TYPE_USB2);
> - struct otg_fsm *fsm;
> int status;
> struct resource *res;
> u32 temp;
> struct fsl_usb2_platform_data *pdata = dev_get_platdata(&pdev->dev);
>
> p_otg = container_of(otg_trans, struct fsl_otg, phy);
> - fsm = &p_otg->fsm;
>
> /* Initialize the state machine structure with default values */
> SET_OTG_STATE(otg_trans, OTG_STATE_UNDEFINED);
> - fsm->otg = p_otg->phy.otg;
>
> /* We don't require predefined MEM/IRQ resource index */
> res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> @@ -964,13 +957,13 @@ int usb_otg_start(struct platform_device *pdev)
> */
> if (fsl_readl(&p_otg->dr_mem_map->otgsc) & OTGSC_STS_USB_ID) {
> p_otg->phy.otg->state = OTG_STATE_UNDEFINED;
> - p_otg->fsm.id = 1;
> + p_otg->otg.fsm.id = 1;
> } else {
> p_otg->phy.otg->state = OTG_STATE_A_IDLE;
> - p_otg->fsm.id = 0;
> + p_otg->otg.fsm.id = 0;
> }
>
> - pr_debug("initial ID pin=%d\n", p_otg->fsm.id);
> + pr_debug("initial ID pin=%d\n", p_otg->otg.fsm.id);
>
> /* enable OTG ID pin interrupt */
> temp = fsl_readl(&p_otg->dr_mem_map->otgsc);
> @@ -987,7 +980,7 @@ int usb_otg_start(struct platform_device *pdev)
> static int show_fsl_usb2_otg_state(struct device *dev,
> struct device_attribute *attr, char *buf)
> {
> - struct otg_fsm *fsm = &fsl_otg_dev->fsm;
> + struct otg_fsm *fsm = &fsl_otg_dev->otg.fsm;
> char *next = buf;
> unsigned size = PAGE_SIZE;
> int t;
> @@ -1085,26 +1078,26 @@ static long fsl_otg_ioctl(struct file *file, unsigned int cmd,
> break;
>
> case SET_A_SUSPEND_REQ:
> - fsl_otg_dev->fsm.a_suspend_req_inf = arg;
> + fsl_otg_dev->otg.fsm.a_suspend_req_inf = arg;
> break;
>
> case SET_A_BUS_DROP:
> - fsl_otg_dev->fsm.a_bus_drop = arg;
> + fsl_otg_dev->otg.fsm.a_bus_drop = arg;
> break;
>
> case SET_A_BUS_REQ:
> - fsl_otg_dev->fsm.a_bus_req = arg;
> + fsl_otg_dev->otg.fsm.a_bus_req = arg;
> break;
>
> case SET_B_BUS_REQ:
> - fsl_otg_dev->fsm.b_bus_req = arg;
> + fsl_otg_dev->otg.fsm.b_bus_req = arg;
> break;
>
> default:
> break;
> }
>
> - otg_statemachine(&fsl_otg_dev->fsm);
> + otg_statemachine(&fsl_otg_dev->otg);
>
> return retval;
> }
> diff --git a/drivers/usb/phy/phy-fsl-usb.h b/drivers/usb/phy/phy-fsl-usb.h
> index 2314995..e207cfb 100644
> --- a/drivers/usb/phy/phy-fsl-usb.h
> +++ b/drivers/usb/phy/phy-fsl-usb.h
> @@ -15,7 +15,6 @@
> * 675 Mass Ave, Cambridge, MA 02139, USA.
> */
>
> -#include <linux/usb/otg-fsm.h>
> #include <linux/usb/otg.h>
> #include <linux/ioctl.h>
>
> @@ -370,7 +369,7 @@ inline struct fsl_otg_timer *otg_timer_initializer
>
> struct fsl_otg {
> struct usb_phy phy;
> - struct otg_fsm fsm;
> + struct usb_otg otg;
> struct usb_dr_mmap *dr_mem_map;
> struct delayed_work otg_event;
>
> diff --git a/include/linux/usb/otg-fsm.h b/include/linux/usb/otg-fsm.h
> index 47b8392..36f0cf9 100644
> --- a/include/linux/usb/otg-fsm.h
> +++ b/include/linux/usb/otg-fsm.h
> @@ -189,7 +189,6 @@ struct otg_fsm {
> int a_bidl_adis_tmout;
>
> struct otg_fsm_ops *ops;
> - struct usb_otg *otg;
>
> /* Current usb protocol used: 0:undefine; 1:host; 2:client */
> int protocol;
> @@ -202,126 +201,23 @@ struct otg_fsm {
> struct device *dev;
> };
>
> +struct usb_otg;
> +
> struct otg_fsm_ops {
> - void (*chrg_vbus)(struct otg_fsm *fsm, int on);
> - void (*drv_vbus)(struct otg_fsm *fsm, int on);
> - void (*loc_conn)(struct otg_fsm *fsm, int on);
> - void (*loc_sof)(struct otg_fsm *fsm, int on);
> - void (*start_pulse)(struct otg_fsm *fsm);
> - void (*start_adp_prb)(struct otg_fsm *fsm);
> - void (*start_adp_sns)(struct otg_fsm *fsm);
> - void (*add_timer)(struct otg_fsm *fsm, enum otg_fsm_timer timer);
> - void (*del_timer)(struct otg_fsm *fsm, enum otg_fsm_timer timer);
> - int (*start_host)(struct otg_fsm *fsm, int on);
> - int (*start_gadget)(struct otg_fsm *fsm, int on);
> + void (*chrg_vbus)(struct usb_otg *otg, int on);
> + void (*drv_vbus)(struct usb_otg *otg, int on);
> + void (*loc_conn)(struct usb_otg *otg, int on);
> + void (*loc_sof)(struct usb_otg *otg, int on);
> + void (*start_pulse)(struct usb_otg *otg);
> + void (*start_adp_prb)(struct usb_otg *otg);
> + void (*start_adp_sns)(struct usb_otg *otg);
> + void (*add_timer)(struct usb_otg *otg, enum otg_fsm_timer timer);
> + void (*del_timer)(struct usb_otg *otg, enum otg_fsm_timer timer);
> + int (*start_host)(struct usb_otg *otg, int on);
> + int (*start_gadget)(struct usb_otg *otg, int on);
> };
>
>
> -static inline int otg_chrg_vbus(struct otg_fsm *fsm, int on)
> -{
> - if (!fsm->ops->chrg_vbus)
> - return -EOPNOTSUPP;
> - fsm->ops->chrg_vbus(fsm, on);
> - return 0;
> -}
> -
> -static inline int otg_drv_vbus(struct otg_fsm *fsm, int on)
> -{
> - if (!fsm->ops->drv_vbus)
> - return -EOPNOTSUPP;
> - if (fsm->drv_vbus != on) {
> - fsm->drv_vbus = on;
> - fsm->ops->drv_vbus(fsm, on);
> - }
> - return 0;
> -}
> -
> -static inline int otg_loc_conn(struct otg_fsm *fsm, int on)
> -{
> - if (!fsm->ops->loc_conn)
> - return -EOPNOTSUPP;
> - if (fsm->loc_conn != on) {
> - fsm->loc_conn = on;
> - fsm->ops->loc_conn(fsm, on);
> - }
> - return 0;
> -}
> -
> -static inline int otg_loc_sof(struct otg_fsm *fsm, int on)
> -{
> - if (!fsm->ops->loc_sof)
> - return -EOPNOTSUPP;
> - if (fsm->loc_sof != on) {
> - fsm->loc_sof = on;
> - fsm->ops->loc_sof(fsm, on);
> - }
> - return 0;
> -}
> -
> -static inline int otg_start_pulse(struct otg_fsm *fsm)
> -{
> - if (!fsm->ops->start_pulse)
> - return -EOPNOTSUPP;
> - if (!fsm->data_pulse) {
> - fsm->data_pulse = 1;
> - fsm->ops->start_pulse(fsm);
> - }
> - return 0;
> -}
> -
> -static inline int otg_start_adp_prb(struct otg_fsm *fsm)
> -{
> - if (!fsm->ops->start_adp_prb)
> - return -EOPNOTSUPP;
> - if (!fsm->adp_prb) {
> - fsm->adp_sns = 0;
> - fsm->adp_prb = 1;
> - fsm->ops->start_adp_prb(fsm);
> - }
> - return 0;
> -}
> -
> -static inline int otg_start_adp_sns(struct otg_fsm *fsm)
> -{
> - if (!fsm->ops->start_adp_sns)
> - return -EOPNOTSUPP;
> - if (!fsm->adp_sns) {
> - fsm->adp_sns = 1;
> - fsm->ops->start_adp_sns(fsm);
> - }
> - return 0;
> -}
> -
> -static inline int otg_add_timer(struct otg_fsm *fsm, enum otg_fsm_timer timer)
> -{
> - if (!fsm->ops->add_timer)
> - return -EOPNOTSUPP;
> - fsm->ops->add_timer(fsm, timer);
> - return 0;
> -}
> -
> -static inline int otg_del_timer(struct otg_fsm *fsm, enum otg_fsm_timer timer)
> -{
> - if (!fsm->ops->del_timer)
> - return -EOPNOTSUPP;
> - fsm->ops->del_timer(fsm, timer);
> - return 0;
> -}
> -
> -static inline int otg_start_host(struct otg_fsm *fsm, int on)
> -{
> - if (!fsm->ops->start_host)
> - return -EOPNOTSUPP;
> - return fsm->ops->start_host(fsm, on);
> -}
> -
> -static inline int otg_start_gadget(struct otg_fsm *fsm, int on)
> -{
> - if (!fsm->ops->start_gadget)
> - return -EOPNOTSUPP;
> - return fsm->ops->start_gadget(fsm, on);
> -}
> -
> -int otg_statemachine(struct otg_fsm *fsm);
> +int otg_statemachine(struct usb_otg *otg);
>
> #endif /* __LINUX_USB_OTG_FSM_H */
> diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h
> index 67929df..e8a14dc 100644
> --- a/include/linux/usb/otg.h
> +++ b/include/linux/usb/otg.h
> @@ -11,6 +11,7 @@
>
> #include <linux/phy/phy.h>
> #include <linux/usb/phy.h>
> +#include <linux/usb/otg-fsm.h>
>
> struct usb_otg {
> u8 default_a;
> @@ -22,6 +23,7 @@ struct usb_otg {
> struct usb_gadget *gadget;
>
> enum usb_otg_state state;
> + struct otg_fsm fsm;
>
> /* bind/unbind the host controller */
> int (*set_host)(struct usb_otg *otg, struct usb_bus *host);
> @@ -128,4 +130,109 @@ enum usb_dr_mode {
> */
> extern enum usb_dr_mode usb_get_dr_mode(struct device *dev);
>
> +static inline int otg_chrg_vbus(struct usb_otg *otg, int on)
> +{
> + if (!otg->fsm.ops->chrg_vbus)
> + return -EOPNOTSUPP;
> + otg->fsm.ops->chrg_vbus(otg, on);
> + return 0;
> +}
> +
> +static inline int otg_drv_vbus(struct usb_otg *otg, int on)
> +{
> + if (!otg->fsm.ops->drv_vbus)
> + return -EOPNOTSUPP;
> + if (otg->fsm.drv_vbus != on) {
> + otg->fsm.drv_vbus = on;
> + otg->fsm.ops->drv_vbus(otg, on);
> + }
> + return 0;
> +}
> +
> +static inline int otg_loc_conn(struct usb_otg *otg, int on)
> +{
> + if (!otg->fsm.ops->loc_conn)
> + return -EOPNOTSUPP;
> + if (otg->fsm.loc_conn != on) {
> + otg->fsm.loc_conn = on;
> + otg->fsm.ops->loc_conn(otg, on);
> + }
> + return 0;
> +}
> +
> +static inline int otg_loc_sof(struct usb_otg *otg, int on)
> +{
> + if (!otg->fsm.ops->loc_sof)
> + return -EOPNOTSUPP;
> + if (otg->fsm.loc_sof != on) {
> + otg->fsm.loc_sof = on;
> + otg->fsm.ops->loc_sof(otg, on);
> + }
> + return 0;
> +}
> +
> +static inline int otg_start_pulse(struct usb_otg *otg)
> +{
> + if (!otg->fsm.ops->start_pulse)
> + return -EOPNOTSUPP;
> + if (!otg->fsm.data_pulse) {
> + otg->fsm.data_pulse = 1;
> + otg->fsm.ops->start_pulse(otg);
> + }
> + return 0;
> +}
> +
> +static inline int otg_start_adp_prb(struct usb_otg *otg)
> +{
> + if (!otg->fsm.ops->start_adp_prb)
> + return -EOPNOTSUPP;
> + if (!otg->fsm.adp_prb) {
> + otg->fsm.adp_sns = 0;
> + otg->fsm.adp_prb = 1;
> + otg->fsm.ops->start_adp_prb(otg);
> + }
> + return 0;
> +}
> +
> +static inline int otg_start_adp_sns(struct usb_otg *otg)
> +{
> + if (!otg->fsm.ops->start_adp_sns)
> + return -EOPNOTSUPP;
> + if (!otg->fsm.adp_sns) {
> + otg->fsm.adp_sns = 1;
> + otg->fsm.ops->start_adp_sns(otg);
> + }
> + return 0;
> +}
> +
> +static inline int otg_add_timer(struct usb_otg *otg, enum otg_fsm_timer timer)
> +{
> + if (!otg->fsm.ops->add_timer)
> + return -EOPNOTSUPP;
> + otg->fsm.ops->add_timer(otg, timer);
> + return 0;
> +}
> +
> +static inline int otg_del_timer(struct usb_otg *otg, enum otg_fsm_timer timer)
> +{
> + if (!otg->fsm.ops->del_timer)
> + return -EOPNOTSUPP;
> + otg->fsm.ops->del_timer(otg, timer);
> + return 0;
> +}
> +
> +static inline int otg_start_host(struct usb_otg *otg, int on)
> +{
> + if (!otg->fsm.ops->start_host)
> + return -EOPNOTSUPP;
> + return otg->fsm.ops->start_host(otg, on);
> +}
> +
> +static inline int otg_start_gadget(struct usb_otg *otg, int on)
> +{
> + if (!otg->fsm.ops->start_gadget)
> + return -EOPNOTSUPP;
> + return otg->fsm.ops->start_gadget(otg, on);
> +}
> +
> #endif /* __LINUX_USB_OTG_H */
> --
> 2.5.0
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-usb" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html

--

Best Regards,
Peter Chen

2016-04-18 08:08:04

by Peter Chen

[permalink] [raw]
Subject: Re: [PATCH v6 04/12] usb: otg-fsm: move host controller operations into usb_otg->hcd_ops

On Tue, Apr 05, 2016 at 05:05:09PM +0300, Roger Quadros wrote:
> This is to prevent missing symbol build error if OTG is
> enabled (built-in) and HCD core (CONFIG_USB) is module.
>
> Signed-off-by: Roger Quadros <[email protected]>

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

> ---
> drivers/usb/chipidea/otg_fsm.c | 7 +++++++
> drivers/usb/common/usb-otg-fsm.c | 15 +++++++++++----
> drivers/usb/phy/phy-fsl-usb.c | 7 +++++++
> include/linux/usb/otg.h | 2 ++
> 4 files changed, 27 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/usb/chipidea/otg_fsm.c b/drivers/usb/chipidea/otg_fsm.c
> index f4e9fb5..5fdf8ca 100644
> --- a/drivers/usb/chipidea/otg_fsm.c
> +++ b/drivers/usb/chipidea/otg_fsm.c
> @@ -582,6 +582,12 @@ static struct otg_fsm_ops ci_otg_ops = {
> .start_gadget = ci_otg_start_gadget,
> };
>
> +static struct otg_hcd_ops ci_hcd_ops = {
> + .usb_bus_start_enum = usb_bus_start_enum,
> + .usb_control_msg = usb_control_msg,
> + .usb_hub_find_child = usb_hub_find_child,
> +};
> +
> int ci_otg_fsm_work(struct ci_hdrc *ci)
> {
> /*
> @@ -805,6 +811,7 @@ int ci_hdrc_otg_fsm_init(struct ci_hdrc *ci)
> ci->otg.state = OTG_STATE_UNDEFINED;
> ci->otg.fsm.ops = &ci_otg_ops;
> ci->otg.fsm.dev = ci->dev;
> + ci->otg.hcd_ops = &ci_hcd_ops;
> ci->gadget.hnp_polling_support = 1;
> ci->otg.fsm.host_req_flag = devm_kzalloc(ci->dev, 1, GFP_KERNEL);
> if (!ci->otg.fsm.host_req_flag)
> diff --git a/drivers/usb/common/usb-otg-fsm.c b/drivers/usb/common/usb-otg-fsm.c
> index abc462c..2cb4aed 100644
> --- a/drivers/usb/common/usb-otg-fsm.c
> +++ b/drivers/usb/common/usb-otg-fsm.c
> @@ -135,11 +135,16 @@ static void otg_hnp_polling_work(struct work_struct *work)
> enum usb_otg_state state = otg->state;
> u8 flag;
> int retval;
> + struct otg_hcd_ops *hcd_ops = otg->hcd_ops;
>
> if (state != OTG_STATE_A_HOST && state != OTG_STATE_B_HOST)
> return;
>
> - udev = usb_hub_find_child(otg->host->root_hub, 1);
> + if (!hcd_ops || !hcd_ops->usb_control_msg ||
> + !hcd_ops->usb_hub_find_child)
> + return;
> +
> + udev = hcd_ops->usb_hub_find_child(otg->host->root_hub, 1);
> if (!udev) {
> dev_err(otg->host->controller,
> "no usb dev connected, can't start HNP polling\n");
> @@ -148,7 +153,7 @@ static void otg_hnp_polling_work(struct work_struct *work)
>
> *fsm->host_req_flag = 0;
> /* Get host request flag from connected USB device */
> - retval = usb_control_msg(udev,
> + retval = hcd_ops->usb_control_msg(udev,
> usb_rcvctrlpipe(udev, 0),
> USB_REQ_GET_STATUS,
> USB_DIR_IN | USB_RECIP_DEVICE,
> @@ -177,7 +182,7 @@ static void otg_hnp_polling_work(struct work_struct *work)
> if (state == OTG_STATE_A_HOST) {
> /* Set b_hnp_enable */
> if (!otg->host->b_hnp_enable) {
> - retval = usb_control_msg(udev,
> + retval = hcd_ops->usb_control_msg(udev,
> usb_sndctrlpipe(udev, 0),
> USB_REQ_SET_FEATURE, 0,
> USB_DEVICE_B_HNP_ENABLE,
> @@ -256,7 +261,9 @@ static int otg_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state)
> otg_loc_conn(otg, 0);
> otg_loc_sof(otg, 1);
> otg_set_protocol(fsm, PROTO_HOST);
> - usb_bus_start_enum(otg->host, otg->host->otg_port);
> + if (otg->hcd_ops && otg->hcd_ops->usb_bus_start_enum)
> + otg->hcd_ops->usb_bus_start_enum(otg->host,
> + otg->host->otg_port);
> otg_start_hnp_polling(fsm);
> break;
> case OTG_STATE_A_IDLE:
> diff --git a/drivers/usb/phy/phy-fsl-usb.c b/drivers/usb/phy/phy-fsl-usb.c
> index a18a2ee..39605d5 100644
> --- a/drivers/usb/phy/phy-fsl-usb.c
> +++ b/drivers/usb/phy/phy-fsl-usb.c
> @@ -792,6 +792,12 @@ static struct otg_fsm_ops fsl_otg_ops = {
> .start_gadget = fsl_otg_start_gadget,
> };
>
> +static struct otg_hcd_ops fsl_hcd_ops = {
> + .usb_bus_start_enum = usb_bus_start_enum,
> + .usb_control_msg = usb_control_msg,
> + .usb_hub_find_child = usb_hub_find_child,
> +};
> +
> /* Initialize the global variable fsl_otg_dev and request IRQ for OTG */
> static int fsl_otg_conf(struct platform_device *pdev)
> {
> @@ -821,6 +827,7 @@ static int fsl_otg_conf(struct platform_device *pdev)
> /* Set OTG state machine operations */
> fsl_otg_tc->otg.fsm.ops = &fsl_otg_ops;
> fsl_otg_tc->otg.fsm.dev = &pdev->dev;
> + fsl_otg_tc->otg.hcd_ops = &fsl_hcd_ops;
>
> /* initialize the otg structure */
> fsl_otg_tc->phy.label = DRIVER_DESC;
> diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h
> index e8a14dc..85b8fb5 100644
> --- a/include/linux/usb/otg.h
> +++ b/include/linux/usb/otg.h
> @@ -12,6 +12,7 @@
> #include <linux/phy/phy.h>
> #include <linux/usb/phy.h>
> #include <linux/usb/otg-fsm.h>
> +#include <linux/usb/hcd.h>
>
> struct usb_otg {
> u8 default_a;
> @@ -24,6 +25,7 @@ struct usb_otg {
>
> enum usb_otg_state state;
> struct otg_fsm fsm;
> + struct otg_hcd_ops *hcd_ops;
>
> /* bind/unbind the host controller */
> int (*set_host)(struct usb_otg *otg, struct usb_bus *host);
> --
> 2.5.0
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-usb" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html

--

Best Regards,
Peter Chen

2016-04-18 08:12:38

by Peter Chen

[permalink] [raw]
Subject: Re: [PATCH v6 06/12] usb: otg: get rid of CONFIG_USB_OTG_FSM in favour of CONFIG_USB_OTG

On Tue, Apr 05, 2016 at 05:05:11PM +0300, Roger Quadros wrote:
> Let's use CONFIG_USB_OTG as a single config option to enable
> USB OTG and the OTG FSM. This makes things a lot less confusing.
>
> Update all users of CONFIG_USB_OTG_FSM to CONFIG_USB_OTG.
>
> Signed-off-by: Roger Quadros <[email protected]>
> ---
> Documentation/usb/chipidea.txt | 2 +-
> drivers/usb/Makefile | 1 +
> drivers/usb/chipidea/Makefile | 2 +-
> drivers/usb/chipidea/ci.h | 2 +-
> drivers/usb/chipidea/otg_fsm.h | 2 +-
> drivers/usb/common/Makefile | 3 ++-
> drivers/usb/core/Kconfig | 8 --------
> drivers/usb/phy/Kconfig | 2 +-
> 8 files changed, 8 insertions(+), 14 deletions(-)
>
> diff --git a/Documentation/usb/chipidea.txt b/Documentation/usb/chipidea.txt
> index 678741b..3b1f263 100644
> --- a/Documentation/usb/chipidea.txt
> +++ b/Documentation/usb/chipidea.txt
> @@ -5,7 +5,7 @@ with 2 Freescale i.MX6Q sabre SD boards.
>
> 1.1 How to enable OTG FSM in menuconfig
> ---------------------------------------
> -Select CONFIG_USB_OTG_FSM, rebuild kernel Image and modules.
> +Select CONFIG_USB_OTG, rebuild kernel Image and modules.
> If you want to check some internal variables for otg fsm,
> mount debugfs, there are 2 files which can show otg fsm
> variables and some controller registers value:
> diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
> index dca7856..16a5b55 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) += common/

Why we need to add above line?

Peter
>
> obj-$(CONFIG_USBIP_CORE) += usbip/
> diff --git a/drivers/usb/chipidea/Makefile b/drivers/usb/chipidea/Makefile
> index 518e445..45aa24d 100644
> --- a/drivers/usb/chipidea/Makefile
> +++ b/drivers/usb/chipidea/Makefile
> @@ -3,7 +3,7 @@ obj-$(CONFIG_USB_CHIPIDEA) += ci_hdrc.o
> ci_hdrc-y := core.o otg.o debug.o
> ci_hdrc-$(CONFIG_USB_CHIPIDEA_UDC) += udc.o
> ci_hdrc-$(CONFIG_USB_CHIPIDEA_HOST) += host.o
> -ci_hdrc-$(CONFIG_USB_OTG_FSM) += otg_fsm.o
> +ci_hdrc-$(CONFIG_USB_OTG) += otg_fsm.o
>
> # Glue/Bridge layers go here
>
> diff --git a/drivers/usb/chipidea/ci.h b/drivers/usb/chipidea/ci.h
> index c523975..1a32b8c 100644
> --- a/drivers/usb/chipidea/ci.h
> +++ b/drivers/usb/chipidea/ci.h
> @@ -406,7 +406,7 @@ static inline u32 hw_test_and_write(struct ci_hdrc *ci, enum ci_hw_regs reg,
> */
> static inline bool ci_otg_is_fsm_mode(struct ci_hdrc *ci)
> {
> -#ifdef CONFIG_USB_OTG_FSM
> +#ifdef CONFIG_USB_OTG
> struct usb_otg_caps *otg_caps = &ci->platdata->ci_otg_caps;
>
> return ci->is_otg && ci->roles[CI_ROLE_HOST] &&
> diff --git a/drivers/usb/chipidea/otg_fsm.h b/drivers/usb/chipidea/otg_fsm.h
> index 6366fe3..2d451bb 100644
> --- a/drivers/usb/chipidea/otg_fsm.h
> +++ b/drivers/usb/chipidea/otg_fsm.h
> @@ -64,7 +64,7 @@
>
> #define TB_AIDL_BDIS (20) /* 4ms ~ 150ms, section 5.2.1 */
>
> -#if IS_ENABLED(CONFIG_USB_OTG_FSM)
> +#if IS_ENABLED(CONFIG_USB_OTG)
>
> int ci_hdrc_otg_fsm_init(struct ci_hdrc *ci);
> int ci_otg_fsm_work(struct ci_hdrc *ci);
> diff --git a/drivers/usb/common/Makefile b/drivers/usb/common/Makefile
> index 6bbb3ec..f8f2c88 100644
> --- a/drivers/usb/common/Makefile
> +++ b/drivers/usb/common/Makefile
> @@ -6,5 +6,6 @@ obj-$(CONFIG_USB_COMMON) += usb-common.o
> usb-common-y += common.o
> usb-common-$(CONFIG_USB_LED_TRIG) += led.o
>
> -obj-$(CONFIG_USB_OTG_FSM) += usb-otg-fsm.o
> obj-$(CONFIG_USB_ULPI_BUS) += ulpi.o
> +usbotg-y := usb-otg-fsm.o
> +obj-$(CONFIG_USB_OTG) += usbotg.o
> diff --git a/drivers/usb/core/Kconfig b/drivers/usb/core/Kconfig
> index dd28010..ae228d0 100644
> --- a/drivers/usb/core/Kconfig
> +++ b/drivers/usb/core/Kconfig
> @@ -75,14 +75,6 @@ config USB_OTG_BLACKLIST_HUB
> and software costs by not supporting external hubs. So
> are "Embedded Hosts" that don't offer OTG support.
>
> -config USB_OTG_FSM
> - tristate "USB 2.0 OTG FSM implementation"
> - depends on USB && USB_OTG
> - select USB_PHY
> - help
> - Implements OTG Finite State Machine as specified in On-The-Go
> - and Embedded Host Supplement to the USB Revision 2.0 Specification.
> -
> config USB_ULPI_BUS
> tristate "USB ULPI PHY interface support"
> depends on USB_SUPPORT
> diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig
> index c690474..06794e2 100644
> --- a/drivers/usb/phy/Kconfig
> +++ b/drivers/usb/phy/Kconfig
> @@ -20,7 +20,7 @@ config AB8500_USB
>
> config FSL_USB2_OTG
> bool "Freescale USB OTG Transceiver Driver"
> - depends on USB_EHCI_FSL && USB_FSL_USB2 && USB_OTG_FSM && PM
> + depends on USB_EHCI_FSL && USB_FSL_USB2 && USB_OTG && PM
> select USB_PHY
> help
> Enable this to support Freescale USB OTG transceiver.
> --
> 2.5.0
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-usb" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html

--

Best Regards,
Peter Chen

2016-04-18 14:11:33

by Alan Stern

[permalink] [raw]
Subject: Re: [PATCH v6 01/12] usb: hcd: Initialize hcd->flags to 0

On Mon, 18 Apr 2016, Peter Chen wrote:

> On Wed, Apr 06, 2016 at 09:32:22AM +0300, Roger Quadros wrote:
> > On 06/04/16 09:09, Felipe Balbi wrote:
> > >
> > > Hi,
> > >
> > > Roger Quadros <[email protected]> writes:
> > >> diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
> > >> index 2ca2cef..6b1930d 100644
> > >> --- a/drivers/usb/core/hcd.c
> > >> +++ b/drivers/usb/core/hcd.c
> > >> @@ -2706,6 +2706,7 @@ int usb_add_hcd(struct usb_hcd *hcd,
> > >> int retval;
> > >> struct usb_device *rhdev;
> > >>
> > >> + hcd->flags = 0;
> > >
> > > seems like this would make more sense in usb_del_hcd() instead.
> > >
> >
> > OK, I'll move it there.
> >
>
> It depends on Alan's comments, whether only usb_add_hcd/usb_del_hcd
> pair can be called repeat. If Alan acks it, I have no idea for it.

Most of the host controller drivers were not written with this in mind,
but I think it would be a good thing to allow. It would speed up the
host/device role switches.

This might mean we need to fix up several drivers to make them work
correctly in an OTG environment. It should be possible to do this. Is
there any particular reason why it would be difficult for Chipidea?

Alan Stern

2016-04-19 02:03:48

by Peter Chen

[permalink] [raw]
Subject: Re: [PATCH v6 01/12] usb: hcd: Initialize hcd->flags to 0

On Mon, Apr 18, 2016 at 10:11:29AM -0400, Alan Stern wrote:
> On Mon, 18 Apr 2016, Peter Chen wrote:
>
> > On Wed, Apr 06, 2016 at 09:32:22AM +0300, Roger Quadros wrote:
> > > On 06/04/16 09:09, Felipe Balbi wrote:
> > > >
> > > > Hi,
> > > >
> > > > Roger Quadros <[email protected]> writes:
> > > >> diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
> > > >> index 2ca2cef..6b1930d 100644
> > > >> --- a/drivers/usb/core/hcd.c
> > > >> +++ b/drivers/usb/core/hcd.c
> > > >> @@ -2706,6 +2706,7 @@ int usb_add_hcd(struct usb_hcd *hcd,
> > > >> int retval;
> > > >> struct usb_device *rhdev;
> > > >>
> > > >> + hcd->flags = 0;
> > > >
> > > > seems like this would make more sense in usb_del_hcd() instead.
> > > >
> > >
> > > OK, I'll move it there.
> > >
> >
> > It depends on Alan's comments, whether only usb_add_hcd/usb_del_hcd
> > pair can be called repeat. If Alan acks it, I have no idea for it.
>
> Most of the host controller drivers were not written with this in mind,
> but I think it would be a good thing to allow. It would speed up the
> host/device role switches.
>
> This might mean we need to fix up several drivers to make them work
> correctly in an OTG environment. It should be possible to do this. Is
> there any particular reason why it would be difficult for Chipidea?
>

I just want to do clean remove at OTG environment, like rmmod, so I did
this when I worked on chipidea OTG design.
I am worried if there are some resources dedicated for host device, eg,
clocks, gpio. etc.

If OTG framework can know well hcd's add and remove, it is ok for chipidea
just calling usb_add_hcd/usb_del_hcd currently, but I suggested roger
adding platform hcd_ops as optional parameter in case the platform
has special requirement for hcd_ops.

--

Best Regards,
Peter Chen

2016-04-19 08:14:21

by Peter Chen

[permalink] [raw]
Subject: Re: [PATCH v6 07/12] usb: otg: add OTG/dual-role core

On Tue, Apr 05, 2016 at 05:05:12PM +0300, Roger Quadros wrote:
> +/**
> + * usb_otg_start_host - start/stop the host controller
> + * @otg: usb_otg instance
> + * @on: true to start, false to stop
> + *
> + * Start/stop the USB host controller. This function is meant
> + * for use by the OTG controller driver.
> + */
> +int usb_otg_start_host(struct usb_otg *otg, int on)
> +{
> + struct otg_hcd_ops *hcd_ops = otg->hcd_ops;
> +
> + dev_dbg(otg->dev, "otg: %s %d\n", __func__, on);
> + if (!otg->host) {
> + WARN_ONCE(1, "otg: fsm running without host\n");
> + return 0;
> + }
> +
> + if (on) {
> + if (otg->flags & OTG_FLAG_HOST_RUNNING)
> + return 0;
> +
> + otg->flags |= OTG_FLAG_HOST_RUNNING;
> +
> + /* start host */
> + hcd_ops->add(otg->primary_hcd.hcd, otg->primary_hcd.irqnum,
> + otg->primary_hcd.irqflags);
> + if (otg->shared_hcd.hcd) {
> + hcd_ops->add(otg->shared_hcd.hcd,
> + otg->shared_hcd.irqnum,
> + otg->shared_hcd.irqflags);
> + }

Check the return value please.

> + } else {
> + if (!(otg->flags & OTG_FLAG_HOST_RUNNING))
> + return 0;
> +
> + otg->flags &= ~OTG_FLAG_HOST_RUNNING;
> +
> + /* stop host */
> + if (otg->shared_hcd.hcd)
> + hcd_ops->remove(otg->shared_hcd.hcd);
> +
> + hcd_ops->remove(otg->primary_hcd.hcd);
> + }
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(usb_otg_start_host);
> +
> +/**
> + * usb_otg_start_gadget - start/stop the gadget controller
> + * @otg: usb_otg instance
> + * @on: true to start, false to stop
> + *
> + * Start/stop the USB gadget controller. This function is meant
> + * for use by the OTG controller driver.
> + */
> +int usb_otg_start_gadget(struct usb_otg *otg, int on)
> +{
> + struct usb_gadget *gadget = otg->gadget;
> +
> + dev_dbg(otg->dev, "otg: %s %d\n", __func__, on);
> + if (!gadget) {
> + WARN_ONCE(1, "otg: fsm running without gadget\n");
> + return 0;
> + }
> +
> + if (on) {
> + if (otg->flags & OTG_FLAG_GADGET_RUNNING)
> + return 0;
> +
> + otg->flags |= OTG_FLAG_GADGET_RUNNING;
> + otg->gadget_ops->start(otg->gadget);

ditto

> + } else {
> + if (!(otg->flags & OTG_FLAG_GADGET_RUNNING))
> + return 0;
> +
> + otg->flags &= ~OTG_FLAG_GADGET_RUNNING;
> + otg->gadget_ops->stop(otg->gadget);
> + }
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(usb_otg_start_gadget);
> +
> +/**
> + * Change USB protocol when there is a protocol change.
> + * fsm->lock must be held.
> + */
> +static int drd_set_protocol(struct otg_fsm *fsm, int protocol)
> +{
> + struct usb_otg *otg = container_of(fsm, struct usb_otg, fsm);
> + int ret = 0;
> +
> + if (fsm->protocol != protocol) {
> + dev_dbg(otg->dev, "otg: changing role fsm->protocol= %d; new protocol= %d\n",
> + fsm->protocol, protocol);
> + /* stop old protocol */
> + if (fsm->protocol == PROTO_HOST)
> + ret = otg_start_host(otg, 0);
> + else if (fsm->protocol == PROTO_GADGET)
> + 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);
> + if (ret)
> + return ret;
> +
> + fsm->protocol = protocol;
> + return 0;
> + }
> +
> + return 0;
> +}
> +
> +/**
> + * Called when entering a DRD state.
> + * fsm->lock must be held.
> + */
> +static void drd_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state)
> +{
> + struct usb_otg *otg = container_of(fsm, struct usb_otg, fsm);
> +
> + if (otg->state == new_state)
> + return;
> +
> + fsm->state_changed = 1;
> + dev_dbg(otg->dev, "otg: set state: %s\n",
> + usb_otg_state_string(new_state));
> + switch (new_state) {
> + case OTG_STATE_B_IDLE:
> + drd_set_protocol(fsm, PROTO_UNDEF);
> + otg_drv_vbus(otg, 0);
> + break;
> + case OTG_STATE_B_PERIPHERAL:
> + drd_set_protocol(fsm, PROTO_GADGET);
> + otg_drv_vbus(otg, 0);
> + break;
> + case OTG_STATE_A_HOST:
> + drd_set_protocol(fsm, PROTO_HOST);
> + otg_drv_vbus(otg, 1);
> + break;
> + 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(otg->dev, "%s: otg: invalid state: %s\n",
> + __func__, usb_otg_state_string(new_state));
> + break;
> + }
> +
> + otg->state = new_state;
> +}
> +
> +/**
> + * DRD state change judgement
> + *
> + * For DRD we're only interested in some of the OTG states
> + * i.e. OTG_STATE_B_IDLE: both peripheral and host are stopped
> + * OTG_STATE_B_PERIPHERAL: peripheral active
> + * OTG_STATE_A_HOST: host active
> + * we're only interested in the following inputs
> + * fsm->id, fsm->b_sess_vld
> + */
> +int drd_statemachine(struct usb_otg *otg)
> +{
> + struct otg_fsm *fsm = &otg->fsm;
> + enum usb_otg_state state;
> + int ret;
> +
> + mutex_lock(&fsm->lock);
> +
> + fsm->state_changed = 0;
> + state = otg->state;
> +
> + switch (state) {
> + case OTG_STATE_UNDEFINED:
> + if (!fsm->id)
> + drd_set_state(fsm, OTG_STATE_A_HOST);
> + else if (fsm->id && fsm->b_sess_vld)
> + drd_set_state(fsm, OTG_STATE_B_PERIPHERAL);
> + else
> + drd_set_state(fsm, OTG_STATE_B_IDLE);
> + break;
> + case OTG_STATE_B_IDLE:
> + if (!fsm->id)
> + drd_set_state(fsm, OTG_STATE_A_HOST);
> + else if (fsm->b_sess_vld)
> + drd_set_state(fsm, OTG_STATE_B_PERIPHERAL);
> + break;
> + case OTG_STATE_B_PERIPHERAL:
> + if (!fsm->id)
> + drd_set_state(fsm, OTG_STATE_A_HOST);
> + else if (!fsm->b_sess_vld)
> + drd_set_state(fsm, OTG_STATE_B_IDLE);
> + break;
> + case OTG_STATE_A_HOST:
> + if (fsm->id && fsm->b_sess_vld)
> + drd_set_state(fsm, OTG_STATE_B_PERIPHERAL);
> + else if (fsm->id && !fsm->b_sess_vld)
> + drd_set_state(fsm, OTG_STATE_B_IDLE);
> + break;
> +
> + /* 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(otg->dev, "%s: otg: invalid usb-drd state: %s\n",
> + __func__, usb_otg_state_string(state));
> + drd_set_state(fsm, OTG_STATE_UNDEFINED);
> + 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);
> +
> +/**
> + * OTG FSM/DRD work function
> + */
> +static void usb_otg_work(struct work_struct *work)
> +{
> + struct usb_otg *otg = container_of(work, struct usb_otg, work);
> +
> + pm_runtime_get_sync(otg->dev);
> + drd_statemachine(otg);
> + pm_runtime_put_sync(otg->dev);
> +}
> +
> +/**
> + * usb_otg_register() - Register the OTG/dual-role device to OTG core
> + * @dev: OTG/dual-role controller device.
> + * @config: OTG configuration.
> + *
> + * Registers the OTG/dual-role controller device with the USB OTG core.
> + *
> + * Return: struct usb_otg * if success, ERR_PTR() if error.
> + */
> +struct usb_otg *usb_otg_register(struct device *dev,
> + struct usb_otg_config *config)
> +{
> + struct usb_otg *otg;
> + struct otg_wait_data *wait;
> + int ret = 0;
> +
> + if (!dev || !config || !config->fsm_ops)
> + return ERR_PTR(-EINVAL);
> +
> + /* already in list? */
> + mutex_lock(&otg_list_mutex);
> + if (usb_otg_get_data(dev)) {
> + dev_err(dev, "otg: %s: device already in otg list\n",
> + __func__);
> + ret = -EINVAL;

Using -EEXIST please

> + goto unlock;
> + }
> +
> + /* allocate and add to list */
> + otg = kzalloc(sizeof(*otg), GFP_KERNEL);
> + if (!otg) {
> + ret = -ENOMEM;
> + goto unlock;
> + }
> +
> + otg->dev = dev;
> + otg->caps = config->otg_caps;
> +
> + if ((otg->caps->hnp_support || otg->caps->srp_support ||
> + otg->caps->adp_support) && !config->otg_work)
> + dev_info(dev, "otg: limiting to dual-role\n");
> +
> + if (config->otg_work) /* custom otg_work ? */
> + INIT_WORK(&otg->work, config->otg_work);
> + else
> + INIT_WORK(&otg->work, usb_otg_work);
> +
> + otg->wq = create_singlethread_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);
> +
> + /* were we in wait list? */
> + mutex_lock(&wait_list_mutex);
> + wait = usb_otg_get_wait(dev);
> + mutex_unlock(&wait_list_mutex);
> + if (wait) {
> + /* register pending host/gadget and flush from list */
> + usb_otg_flush_wait(dev);
> + }
> +
> + return otg;
> +
> +err_wq:
> + kfree(otg);
> +unlock:
> + mutex_unlock(&otg_list_mutex);
> + return ERR_PTR(ret);
> +}
> +EXPORT_SYMBOL_GPL(usb_otg_register);
> +
> +/**
> + * usb_otg_unregister() - Unregister the OTG/dual-role device from USB OTG core
> + * @dev: OTG controller device.
> + *
> + * Unregisters the OTG/dual-role controller device from USB OTG core.
> + * Prevents unregistering till both the associated Host and Gadget controllers
> + * have unregistered from the OTG core.
> + *
> + * Return: 0 on success, error value otherwise.
> + */
> +int usb_otg_unregister(struct device *dev)
> +{
> + struct usb_otg *otg;
> +
> + mutex_lock(&otg_list_mutex);
> + otg = usb_otg_get_data(dev);
> + if (!otg) {
> + dev_err(dev, "otg: %s: device not in otg list\n",
> + __func__);
> + mutex_unlock(&otg_list_mutex);
> + return -EINVAL;
> + }
> +
> + /* prevent unregister till both host & gadget have unregistered */
> + if (otg->host || otg->gadget) {
> + dev_err(dev, "otg: %s: host/gadget still registered\n",
> + __func__);
> + return -EBUSY;
> + }
> +
> + /* OTG FSM is halted when host/gadget unregistered */
> + destroy_workqueue(otg->wq);
> +
> + /* remove from otg list */
> + list_del(&otg->list);
> + kfree(otg);
> + mutex_unlock(&otg_list_mutex);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(usb_otg_unregister);
> +
> +/**
> + * start/kick the OTG FSM if we can
> + * fsm->lock must be held
> + */
> +static void usb_otg_start_fsm(struct usb_otg *otg)
> +{
> + struct otg_fsm *fsm = &otg->fsm;
> +
> + if (fsm->running)
> + goto kick_fsm;
> +
> + if (!otg->host) {
> + dev_info(otg->dev, "otg: can't start till host registers\n");
> + return;
> + }

dev_err

> +
> + if (!otg->gadget) {
> + dev_info(otg->dev, "otg: can't start till gadget registers\n");
> + return;
> + }

dev_err

> +
> + fsm->running = true;
> +kick_fsm:
> + queue_work(otg->wq, &otg->work);
> +}
> +
> +/**
> + * stop the OTG FSM. Stops Host & Gadget controllers as well.
> + * fsm->lock must be held
> + */
> +static void usb_otg_stop_fsm(struct usb_otg *otg)
> +{
> + struct otg_fsm *fsm = &otg->fsm;
> +
> + if (!fsm->running)
> + return;
> +
> + /* no more new events queued */
> + fsm->running = false;
> +
> + flush_workqueue(otg->wq);
> + otg->state = OTG_STATE_UNDEFINED;
> +
> + /* stop host/gadget immediately */
> + if (fsm->protocol == PROTO_HOST)
> + otg_start_host(otg, 0);
> + else if (fsm->protocol == PROTO_GADGET)
> + otg_start_gadget(otg, 0);
> + fsm->protocol = PROTO_UNDEF;
> +}
> +
> +/**
> + * usb_otg_sync_inputs - Sync OTG inputs with the OTG state machine
> + * @fsm: OTG FSM instance
> + *
> + * Used by the OTG driver to update the inputs to the OTG
> + * state machine.
> + *
> + * Can be called in IRQ context.
> + */
> +void usb_otg_sync_inputs(struct usb_otg *otg)
> +{
> + /* Don't kick FSM till it has started */
> + if (!otg->fsm.running)
> + return;
> +
> + /* Kick FSM */
> + queue_work(otg->wq, &otg->work);
> +}
> +EXPORT_SYMBOL_GPL(usb_otg_sync_inputs);
> +
> +/**
> + * usb_otg_kick_fsm - Kick the OTG state machine
> + * @otg_dev: OTG controller device
> + *
> + * Used by USB host/device stack to sync OTG related
> + * events to the OTG state machine.
> + * e.g. change in host_bus->b_hnp_enable, gadget->b_hnp_enable
> + *
> + * Returns: 0 on success, error value otherwise.
> + */
> +int usb_otg_kick_fsm(struct device *otg_dev)
> +{
> + struct usb_otg *otg;
> +
> + mutex_lock(&otg_list_mutex);
> + otg = usb_otg_get_data(otg_dev);
> + mutex_unlock(&otg_list_mutex);
> + if (!otg) {
> + dev_dbg(otg_dev, "otg: %s: invalid otg device\n",
> + __func__);
> + return -ENODEV;
> + }
> +
> + usb_otg_sync_inputs(otg);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(usb_otg_kick_fsm);
> +
> +/**
> + * usb_otg_register_hcd - Register the host controller to OTG core
> + * @hcd: host controller device
> + * @irqnum: interrupt number
> + * @irqflags: interrupt flags
> + * @ops: HCD ops to interface with the HCD
> + *
> + * This is used by the USB Host stack to register the host controller
> + * to the OTG core. Host controller must not be started by the
> + * caller as it is left upto the OTG state machine to do so.

%s/upto/up to

> + * hcd->otg_dev must contain the related otg controller device.
> + *
> + * Returns: 0 on success, error value otherwise.
> + */
> +int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
> + unsigned long irqflags, struct otg_hcd_ops *ops)
> +{
> + struct usb_otg *otg;
> + struct device *hcd_dev = hcd->self.controller;
> + struct device *otg_dev = hcd->otg_dev;
> +
> + if (!otg_dev)
> + return -EINVAL;
> +
> + /* we're otg but otg controller might not yet be registered */
> + mutex_lock(&otg_list_mutex);
> + otg = usb_otg_get_data(otg_dev);
> + mutex_unlock(&otg_list_mutex);
> + if (!otg) {
> + dev_dbg(hcd_dev,
> + "otg: controller not yet registered. waiting..\n");
> + /*
> + * otg controller might register later. Put the hcd in
> + * wait list and call us back when ready
> + */
> + if (usb_otg_hcd_wait_add(otg_dev, hcd, irqnum, irqflags, ops)) {
> + dev_err(hcd_dev, "otg: failed to add hcd to wait list\n");
> + return -EINVAL;
> + }
> +
> + return 0;
> + }
> +
> + /* 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) {
> + /* are we in wait list? */
> + if (!usb_otg_hcd_wait_remove(hcd))
> + return 0;
> +
> + dev_dbg(hcd_dev, "otg: host wasn't registered with otg\n");
> + return -EINVAL;
> + }
> +
> + mutex_lock(&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 {
> + dev_err(otg_dev, "otg: host %s wasn't registered with otg\n",
> + dev_name(hcd_dev));
> + mutex_unlock(&otg->fsm.lock);
> + return -EINVAL;
> + }
> +
> + /* stop FSM & Host */
> + usb_otg_stop_fsm(otg);
> + otg->host = NULL;
> +
> + mutex_unlock(&otg->fsm.lock);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(usb_otg_unregister_hcd);
> +
> +/**
> + * usb_otg_register_gadget - Register the gadget controller to OTG core
> + * @gadget: gadget controller
> + *
> + * This is used by the USB gadget stack to register the gadget controller
> + * to the OTG core. Gadget controller must not be started by the
> + * caller as it is left upto the OTG state machine to do so.

%s/upto/up to

> + *
> + * Gadget core must call this only when all resources required for
> + * gadget controller to run are available.
> + * i.e. gadget function driver is available.
> + *
> + * Returns: 0 on success, error value otherwise.
> + */
> +int usb_otg_register_gadget(struct usb_gadget *gadget,
> + struct otg_gadget_ops *ops)
> +{
> + struct usb_otg *otg;
> + struct device *gadget_dev = &gadget->dev;
> + struct device *otg_dev = gadget->otg_dev;
> +
> + if (!otg_dev)
> + return -EINVAL; /* we're definitely not OTG */
> +
> + /* we're otg but otg controller might not yet be registered */
> + mutex_lock(&otg_list_mutex);
> + otg = usb_otg_get_data(otg_dev);
> + mutex_unlock(&otg_list_mutex);
> + if (!otg) {
> + dev_dbg(gadget_dev,
> + "otg: controller not yet registered. waiting..\n");
> + /*
> + * otg controller might register later. Put the gadget in
> + * wait list and call us back when ready
> + */
> + if (usb_otg_gadget_wait_add(otg_dev, gadget, ops)) {
> + dev_err(gadget_dev,
> + "otg: failed to add to gadget to wait list\n");
> + return -EINVAL;
> + }
> +
> + return 0;
> + }
> +
> + 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));
> +
> + /* start FSM */
> + usb_otg_start_fsm(otg);
> + mutex_unlock(&otg->fsm.lock);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(usb_otg_register_gadget);
> +
> +/**
> + * usb_otg_unregister_gadget - Unregister the gadget controller from OTG core
> + * @gadget: gadget controller
> + *
> + * This is used by the USB gadget stack to unregister the gadget controller
> + * from the OTG core. Ensures that gadget controller is halted
> + * on successful return.
> + *
> + * Returns: 0 on success, error value otherwise.
> + */
> +int usb_otg_unregister_gadget(struct usb_gadget *gadget)
> +{
> + struct usb_otg *otg;
> + struct device *gadget_dev = &gadget->dev;
> + struct device *otg_dev = gadget->otg_dev;
> +
> + if (!otg_dev)
> + return -EINVAL;
> +
> + mutex_lock(&otg_list_mutex);
> + otg = usb_otg_get_data(otg_dev);
> + mutex_unlock(&otg_list_mutex);
> + if (!otg) {
> + /* are we in wait list? */
> + if (!usb_otg_gadget_wait_remove(gadget))
> + return 0;
> +
> + dev_dbg(gadget_dev, "otg: gadget wasn't registered with otg\n");
> + return -EINVAL;
> + }
> +
> + mutex_lock(&otg->fsm.lock);
> + if (otg->gadget != gadget) {
> + dev_err(otg_dev, "otg: gadget %s wasn't registered with otg\n",
> + dev_name(&gadget->dev));
> + mutex_unlock(&otg->fsm.lock);
> + return -EINVAL;
> + }
> +
> + /* Stop FSM & gadget */
> + 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);
> diff --git a/drivers/usb/common/usb-otg.h b/drivers/usb/common/usb-otg.h
> new file mode 100644
> index 0000000..2bf3fbf
> --- /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) 2016 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)
> +

Since this set is mainly for DRD, these FSM stuffs aren't needed.
Besides, there is already an otg-fsm.h at include/linux/usb/, and
there are both otg and otg-fsm source and headers, we may let these
timing thing belong to otg-fsm.

> +#endif /* __DRIVERS_USB_COMMON_USB_OTG_H */
> diff --git a/drivers/usb/core/Kconfig b/drivers/usb/core/Kconfig
> index ae228d0..b468a9f 100644
> --- a/drivers/usb/core/Kconfig
> +++ b/drivers/usb/core/Kconfig
> @@ -42,7 +42,7 @@ config USB_DYNAMIC_MINORS
> If you are unsure about this, say N here.
>
> config USB_OTG
> - bool "OTG support"
> + bool "OTG/Dual-role support"
> depends on PM
> default n
> help
> diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h
> index 8c0ae64..1878ae1 100644
> --- a/include/linux/usb/gadget.h
> +++ b/include/linux/usb/gadget.h
> @@ -583,6 +583,7 @@ struct usb_gadget_ops {
> * @out_epnum: last used out ep number
> * @in_epnum: last used in ep number
> * @otg_caps: OTG capabilities of this gadget.
> + * @otg_dev: OTG controller device, if needs to be used with OTG core.
> * @sg_supported: true if we can handle scatter-gather
> * @is_otg: True if the USB device port uses a Mini-AB jack, so that the
> * gadget driver must provide a USB OTG descriptor.
> @@ -639,6 +640,7 @@ struct usb_gadget {
> unsigned out_epnum;
> unsigned in_epnum;
> struct usb_otg_caps *otg_caps;
> + struct device *otg_dev;
>
> unsigned sg_supported:1;
> unsigned is_otg:1;
> diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h
> index 861ccaa..2017cd4 100644
> --- a/include/linux/usb/hcd.h
> +++ b/include/linux/usb/hcd.h
> @@ -184,6 +184,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 36f0cf9..ba6755c 100644
> --- a/include/linux/usb/otg-fsm.h
> +++ b/include/linux/usb/otg-fsm.h
> @@ -61,6 +61,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
> @@ -133,6 +138,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 */
> @@ -188,6 +194,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..b094352 100644
> --- a/include/linux/usb/otg.h
> +++ b/include/linux/usb/otg.h
> @@ -10,10 +10,55 @@
> #define __LINUX_USB_OTG_H
>
> #include <linux/phy/phy.h>
> -#include <linux/usb/phy.h>
> -#include <linux/usb/otg-fsm.h>
> +#include <linux/device.h>
> +#include <linux/hrtimer.h>
> +#include <linux/ktime.h>

Does above two headers are really needed?

> +#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>

Does above header is really needed?

>
> +/**
> + * struct otg_hcd - host controller state and interface
> + *
> + * @hcd: host controller
> + * @irqnum: irq number
> + * @irqflags: irq flags
> + * @ops: otg to host controller interface
> + * @ops: otg to host controller interface

Duplicated line

--

Best Regards,
Peter Chen

2016-04-19 08:22:14

by Peter Chen

[permalink] [raw]
Subject: Re: [PATCH v6 08/12] usb: hcd: Adapt to OTG core

On Mon, Apr 18, 2016 at 02:29:37PM +0800, Peter Chen wrote:
> On Tue, Apr 05, 2016 at 05:05:13PM +0300, Roger Quadros wrote:
> > Introduce usb_otg_add/remove_hcd() for use by host
> > controllers that are part of OTG/dual-role port.
> >
> > Non Device tree platforms can use the otg_dev argument
> > to specify the OTG controller device. If otg_dev is NULL
> > then the device tree node's otg-controller property is used to
> > get the otg_dev device.
> >
> > Signed-off-by: Roger Quadros <[email protected]>
> > ---
> > drivers/usb/core/hcd.c | 71 +++++++++++++++++++++++++++++++++++++++++++++++++
> > include/linux/usb/hcd.h | 4 +++
> > 2 files changed, 75 insertions(+)
> >
> > diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
> > index 6b1930d..6a80193 100644
> > --- a/drivers/usb/core/hcd.c
> > +++ b/drivers/usb/core/hcd.c
> > @@ -46,6 +46,10 @@
> > #include <linux/usb.h>
> > #include <linux/usb/hcd.h>
> > #include <linux/usb/phy.h>
> > +#include <linux/usb/otg.h>
> > +
> > +#include <linux/of.h>
> > +#include <linux/of_platform.h>
> >
> > #include "usb.h"
> >
> > @@ -3013,6 +3017,73 @@ void usb_remove_hcd(struct usb_hcd *hcd)
> > }
> > EXPORT_SYMBOL_GPL(usb_remove_hcd);
> >
> > +
> > +static struct otg_hcd_ops otg_hcd_intf = {
> > + .add = usb_add_hcd,
> > + .remove = usb_remove_hcd,
> > + .usb_bus_start_enum = usb_bus_start_enum,
> > + .usb_control_msg = usb_control_msg,
> > + .usb_hub_find_child = usb_hub_find_child,
> > +};
> > +
> > +/**
> > + * usb_otg_add_hcd - Register the HCD with OTG core.
> > + * @hcd: the usb_hcd structure to initialize
> > + * @irqnum: Interrupt line to allocate
> > + * @irqflags: Interrupt type flags
> > + * @otg_dev: OTG controller device manging this HCD
>
> %s/manging/managing
>
> > + *
> > + * Registers the HCD with OTG core. OTG core will call usb_add_hcd()
> > + * or usb_remove_hcd() as necessary.
> > + * If otg_dev is NULL then device tree node is checked for OTG
> > + * controller device via the otg-controller property.
> > + */
> > +int usb_otg_add_hcd(struct usb_hcd *hcd,
> > + unsigned int irqnum, unsigned long irqflags,
> > + struct device *otg_dev)
> > +{
>
> Some users may not want to use default otg_hcd_ops, would you please
> add one more parameter (eg, hcd_ops) for that? If hcd_ops is NULL, just
> use the default one.
>

I forgot there is .start_host at otg fsm API when reviewing, so it is
ok to have a unified hcd_ops, the user can do platform things at
fsm.start_host.

--

Best Regards,
Peter Chen

2016-04-19 09:25:36

by Peter Chen

[permalink] [raw]
Subject: Re: [PATCH v6 07/12] usb: otg: add OTG/dual-role core

On Fri, Apr 15, 2016 at 10:03:16AM +0000, Yoshihiro Shimoda wrote:
> Hi,
>
> > From: Yoshihiro Shimoda
> > Sent: Friday, April 15, 2016 6:59 PM
> >
> > Hi,
> >
> > > From: Roger Quadros
> > > Sent: Thursday, April 14, 2016 8:32 PM
> > >
> > > On 14/04/16 14:15, Yoshihiro Shimoda wrote:
> > > > Hi,
> > > >
> > < snip >
> > > >>> @@ -865,7 +867,8 @@ 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->caps->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 */
> > > >>>
> > > >>
> > > >> These changes look good to me. Thanks.
> > > >
> > > > Thank you for the comment.
> > > > If we change the "needs_companion" place to the otg_config,
> > > > do we need to add a flag into the otg, instead of otg->caps?
> > >
> > > Yes we can add a flag in struct usb_otg.
> >
> > Thank you for the comment.
> >
> > I made a fixed patch.
> > So, should I send this patch to ML after you sent v7 patches?
> > Or, would you apply this patch before you send v7 patches?
>
> Oops, I sent this email without my patch...
>
> ---
> Subject: [PATCH] usb: otg: add hcd companion support
>
> 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 <[email protected]>
> ---
> Documentation/devicetree/bindings/usb/generic.txt | 3 +++
> drivers/usb/common/usb-otg.c | 17 +++++++++++++----
> include/linux/usb/otg.h | 7 ++++++-
> 3 files changed, 22 insertions(+), 5 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 41e762a..83c8c96 100644
> --- a/drivers/usb/common/usb-otg.c
> +++ b/drivers/usb/common/usb-otg.c
> @@ -20,6 +20,7 @@
> #include <linux/list.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>
> @@ -600,6 +601,10 @@ struct usb_otg *usb_otg_register(struct device *dev,
> else
> INIT_WORK(&otg->work, usb_otg_work);
>
> + if (of_find_property(dev->of_node, "hcd-needs-companion", NULL) ||
> + config->hcd_needs_companion) /* needs comanion ? */

%s/comanion/companion

I have a little puzzled with companion controller and shared hcd, let me
post a topic for it.

Peter

> + otg->flags |= OTG_FLAG_HCD_NEEDS_COMPANION;
> +
> otg->wq = create_singlethread_workqueue("usb_otg");
> if (!otg->wq) {
> dev_err(dev, "otg: %s: can't create workqueue\n",
> @@ -823,13 +828,15 @@ 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");
> goto err;
> @@ -865,7 +872,9 @@ 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 */
>
> diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h
> index b094352..6f4ca77 100644
> --- a/include/linux/usb/otg.h
> +++ b/include/linux/usb/otg.h
> @@ -57,7 +57,8 @@ struct otg_hcd {
> * @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;
> @@ -84,6 +85,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 -----------------------------*/
> @@ -125,11 +127,14 @@ struct usb_otg_caps {
> * @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);
> --
> 1.9.1
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-usb" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html

--

Best Regards,
Peter Chen

2016-04-20 05:08:49

by Yoshihiro Shimoda

[permalink] [raw]
Subject: RE: [PATCH v6 07/12] usb: otg: add OTG/dual-role core

Hi,

> From: Peter Chen
> Sent: Tuesday, April 19, 2016 6:18 PM
>
> On Fri, Apr 15, 2016 at 10:03:16AM +0000, Yoshihiro Shimoda wrote:
> > Hi,
> >
> > > From: Yoshihiro Shimoda
> > > Sent: Friday, April 15, 2016 6:59 PM
> > >
> > > Hi,
> > >
> > > > From: Roger Quadros
> > > > Sent: Thursday, April 14, 2016 8:32 PM
> > > >
> > > > On 14/04/16 14:15, Yoshihiro Shimoda wrote:
> > > > > Hi,
> > > > >
> > > < snip >
> > > > >>> @@ -865,7 +867,8 @@ 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->caps->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 */
> > > > >>>
> > > > >>
> > > > >> These changes look good to me. Thanks.
> > > > >
> > > > > Thank you for the comment.
> > > > > If we change the "needs_companion" place to the otg_config,
> > > > > do we need to add a flag into the otg, instead of otg->caps?
> > > >
> > > > Yes we can add a flag in struct usb_otg.
> > >
> > > Thank you for the comment.
> > >
> > > I made a fixed patch.
> > > So, should I send this patch to ML after you sent v7 patches?
> > > Or, would you apply this patch before you send v7 patches?
> >
> > Oops, I sent this email without my patch...
> >
> > ---
> > Subject: [PATCH] usb: otg: add hcd companion support
> >
> > 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 <[email protected]>
> > ---
> > Documentation/devicetree/bindings/usb/generic.txt | 3 +++
> > drivers/usb/common/usb-otg.c | 17 +++++++++++++----
> > include/linux/usb/otg.h | 7 ++++++-
> > 3 files changed, 22 insertions(+), 5 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 41e762a..83c8c96 100644
> > --- a/drivers/usb/common/usb-otg.c
> > +++ b/drivers/usb/common/usb-otg.c
> > @@ -20,6 +20,7 @@
> > #include <linux/list.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>
> > @@ -600,6 +601,10 @@ struct usb_otg *usb_otg_register(struct device *dev,
> > else
> > INIT_WORK(&otg->work, usb_otg_work);
> >
> > + if (of_find_property(dev->of_node, "hcd-needs-companion", NULL) ||
> > + config->hcd_needs_companion) /* needs comanion ? */
>
> %s/comanion/companion

Thank you for pointing it out!

Roger, would you fix this in your v7 patch set?

> I have a little puzzled with companion controller and shared hcd, let me
> post a topic for it.

I looked at the email thread.
It is very useful information to me! :)

Best regards,
Yoshihiro Shimoda

> Peter
>
> > + otg->flags |= OTG_FLAG_HCD_NEEDS_COMPANION;
> > +
> > otg->wq = create_singlethread_workqueue("usb_otg");
> > if (!otg->wq) {
> > dev_err(dev, "otg: %s: can't create workqueue\n",
> > @@ -823,13 +828,15 @@ 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");
> > goto err;
> > @@ -865,7 +872,9 @@ 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 */
> >
> > diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h
> > index b094352..6f4ca77 100644
> > --- a/include/linux/usb/otg.h
> > +++ b/include/linux/usb/otg.h
> > @@ -57,7 +57,8 @@ struct otg_hcd {
> > * @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;
> > @@ -84,6 +85,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 -----------------------------*/
> > @@ -125,11 +127,14 @@ struct usb_otg_caps {
> > * @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);
> > --
> > 1.9.1
> >
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-usb" in
> > the body of a message to [email protected]
> > More majordomo info at http://vger.kernel.org/majordomo-info.html
>
> --
>
> Best Regards,
> Peter Chen

2016-04-20 06:47:16

by Roger Quadros

[permalink] [raw]
Subject: Re: [PATCH v6 08/12] usb: hcd: Adapt to OTG core

On 18/04/16 09:29, Peter Chen wrote:
> On Tue, Apr 05, 2016 at 05:05:13PM +0300, Roger Quadros wrote:
>> Introduce usb_otg_add/remove_hcd() for use by host
>> controllers that are part of OTG/dual-role port.
>>
>> Non Device tree platforms can use the otg_dev argument
>> to specify the OTG controller device. If otg_dev is NULL
>> then the device tree node's otg-controller property is used to
>> get the otg_dev device.
>>
>> Signed-off-by: Roger Quadros <[email protected]>
>> ---
>> drivers/usb/core/hcd.c | 71 +++++++++++++++++++++++++++++++++++++++++++++++++
>> include/linux/usb/hcd.h | 4 +++
>> 2 files changed, 75 insertions(+)
>>
>> diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
>> index 6b1930d..6a80193 100644
>> --- a/drivers/usb/core/hcd.c
>> +++ b/drivers/usb/core/hcd.c
>> @@ -46,6 +46,10 @@
>> #include <linux/usb.h>
>> #include <linux/usb/hcd.h>
>> #include <linux/usb/phy.h>
>> +#include <linux/usb/otg.h>
>> +
>> +#include <linux/of.h>
>> +#include <linux/of_platform.h>
>>
>> #include "usb.h"
>>
>> @@ -3013,6 +3017,73 @@ void usb_remove_hcd(struct usb_hcd *hcd)
>> }
>> EXPORT_SYMBOL_GPL(usb_remove_hcd);
>>
>> +
>> +static struct otg_hcd_ops otg_hcd_intf = {
>> + .add = usb_add_hcd,
>> + .remove = usb_remove_hcd,
>> + .usb_bus_start_enum = usb_bus_start_enum,
>> + .usb_control_msg = usb_control_msg,
>> + .usb_hub_find_child = usb_hub_find_child,
>> +};
>> +
>> +/**
>> + * usb_otg_add_hcd - Register the HCD with OTG core.
>> + * @hcd: the usb_hcd structure to initialize
>> + * @irqnum: Interrupt line to allocate
>> + * @irqflags: Interrupt type flags
>> + * @otg_dev: OTG controller device manging this HCD
>
> %s/manging/managing

OK.

>
>> + *
>> + * Registers the HCD with OTG core. OTG core will call usb_add_hcd()
>> + * or usb_remove_hcd() as necessary.
>> + * If otg_dev is NULL then device tree node is checked for OTG
>> + * controller device via the otg-controller property.
>> + */
>> +int usb_otg_add_hcd(struct usb_hcd *hcd,
>> + unsigned int irqnum, unsigned long irqflags,
>> + struct device *otg_dev)
>> +{
>
> Some users may not want to use default otg_hcd_ops, would you please
> add one more parameter (eg, hcd_ops) for that? If hcd_ops is NULL, just
> use the default one.
>

Can we please wait to add anything new till we have such users?

>> + struct device *dev = hcd->self.controller;
>> +
>> + if (!otg_dev) {
>> + struct device_node *np;
>> + struct platform_device *pdev;
>> +
>> + np = of_parse_phandle(dev->of_node, "otg-controller", 0);
>> + if (!np) {
>> + dev_err(dev,
>> + "otg_dev is NULL and no otg-controller property in DT\n");
>> + return -EINVAL;
>> + }
>> +
>> + pdev = of_find_device_by_node(np);
>> + of_node_put(np);
>> + if (!pdev) {
>> + dev_err(dev,
>> + "couldn't get otg-controller device\n");
>> + return -ENODEV;
>> + }
>> +
>> + hcd->otg_dev = &pdev->dev;
>> + } else {
>> + hcd->otg_dev = otg_dev;
>> + }
>> +
>> + return usb_otg_register_hcd(hcd, irqnum, irqflags, &otg_hcd_intf);
>> +}
>> +EXPORT_SYMBOL_GPL(usb_otg_add_hcd);
>> +
>

--
cheers,
-roger

2016-04-20 06:48:21

by Roger Quadros

[permalink] [raw]
Subject: Re: [PATCH v6 08/12] usb: hcd: Adapt to OTG core

On 19/04/16 11:14, Peter Chen wrote:
> On Mon, Apr 18, 2016 at 02:29:37PM +0800, Peter Chen wrote:
>> On Tue, Apr 05, 2016 at 05:05:13PM +0300, Roger Quadros wrote:
>>> Introduce usb_otg_add/remove_hcd() for use by host
>>> controllers that are part of OTG/dual-role port.
>>>
>>> Non Device tree platforms can use the otg_dev argument
>>> to specify the OTG controller device. If otg_dev is NULL
>>> then the device tree node's otg-controller property is used to
>>> get the otg_dev device.
>>>
>>> Signed-off-by: Roger Quadros <[email protected]>
>>> ---
>>> drivers/usb/core/hcd.c | 71 +++++++++++++++++++++++++++++++++++++++++++++++++
>>> include/linux/usb/hcd.h | 4 +++
>>> 2 files changed, 75 insertions(+)
>>>
>>> diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
>>> index 6b1930d..6a80193 100644
>>> --- a/drivers/usb/core/hcd.c
>>> +++ b/drivers/usb/core/hcd.c
>>> @@ -46,6 +46,10 @@
>>> #include <linux/usb.h>
>>> #include <linux/usb/hcd.h>
>>> #include <linux/usb/phy.h>
>>> +#include <linux/usb/otg.h>
>>> +
>>> +#include <linux/of.h>
>>> +#include <linux/of_platform.h>
>>>
>>> #include "usb.h"
>>>
>>> @@ -3013,6 +3017,73 @@ void usb_remove_hcd(struct usb_hcd *hcd)
>>> }
>>> EXPORT_SYMBOL_GPL(usb_remove_hcd);
>>>
>>> +
>>> +static struct otg_hcd_ops otg_hcd_intf = {
>>> + .add = usb_add_hcd,
>>> + .remove = usb_remove_hcd,
>>> + .usb_bus_start_enum = usb_bus_start_enum,
>>> + .usb_control_msg = usb_control_msg,
>>> + .usb_hub_find_child = usb_hub_find_child,
>>> +};
>>> +
>>> +/**
>>> + * usb_otg_add_hcd - Register the HCD with OTG core.
>>> + * @hcd: the usb_hcd structure to initialize
>>> + * @irqnum: Interrupt line to allocate
>>> + * @irqflags: Interrupt type flags
>>> + * @otg_dev: OTG controller device manging this HCD
>>
>> %s/manging/managing
>>
>>> + *
>>> + * Registers the HCD with OTG core. OTG core will call usb_add_hcd()
>>> + * or usb_remove_hcd() as necessary.
>>> + * If otg_dev is NULL then device tree node is checked for OTG
>>> + * controller device via the otg-controller property.
>>> + */
>>> +int usb_otg_add_hcd(struct usb_hcd *hcd,
>>> + unsigned int irqnum, unsigned long irqflags,
>>> + struct device *otg_dev)
>>> +{
>>
>> Some users may not want to use default otg_hcd_ops, would you please
>> add one more parameter (eg, hcd_ops) for that? If hcd_ops is NULL, just
>> use the default one.
>>
>
> I forgot there is .start_host at otg fsm API when reviewing, so it is
> ok to have a unified hcd_ops, the user can do platform things at
> fsm.start_host.
>
OK, great :).

cheers,
-roger

2016-04-20 06:51:37

by Roger Quadros

[permalink] [raw]
Subject: Re: [PATCH v6 09/12] usb: gadget: udc: adapt to OTG core

On 18/04/16 09:59, Peter Chen wrote:
> On Tue, Apr 05, 2016 at 05:05:14PM +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().
>>
>> Introduce usb_otg_add_gadget_udc() to allow controller drivers
>> to register a gadget controller that is part of an OTG instance.
>>
>> Register with OTG core when gadget function driver
>> is available and unregister when function driver is unbound.
>>
>> We need to unlock the usb_lock mutexbefore calling
>
> ...mutex before...

OK.
>
>> usb_otg_register_gadget() in udc_bind_to_driver() and
>> usb_gadget_remove_driver() else it will cause a circular
>> locking dependency.
>>
>> Ignore softconnect sysfs control when we're in OTG
>> mode as OTG FSM takes care of gadget softconnect using
>> the b_bus_req mechanism.
>>
>> Signed-off-by: Roger Quadros <[email protected]>
>> ---
>> drivers/usb/gadget/udc/udc-core.c | 166 +++++++++++++++++++++++++++++++++++---
>> include/linux/usb/gadget.h | 4 +
>> 2 files changed, 161 insertions(+), 9 deletions(-)
>>
>> diff --git a/drivers/usb/gadget/udc/udc-core.c b/drivers/usb/gadget/udc/udc-core.c
>> index 4151597..9b9702f 100644
>> --- a/drivers/usb/gadget/udc/udc-core.c
>> +++ b/drivers/usb/gadget/udc/udc-core.c
>> @@ -28,6 +28,10 @@
>> #include <linux/usb/ch9.h>
>> #include <linux/usb/gadget.h>
>> #include <linux/usb.h>
>> +#include <linux/usb/otg.h>
>> +
>> +#include <linux/of.h>
>> +#include <linux/of_platform.h>
>>
>> /**
>> * struct usb_udc - describes one usb device controller
>> @@ -304,6 +308,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);
>> }
>
> You may delete the debug message next time.

OK for this and all the next comments to remove debug messages.

>
>>
>> @@ -321,10 +326,81 @@ static inline int usb_gadget_udc_start(struct usb_udc *udc)
>> */
>> static inline void usb_gadget_udc_stop(struct usb_udc *udc)
>> {
>> + dev_dbg(&udc->dev, "%s\n", __func__);
>> udc->gadget->ops->udc_stop(udc->gadget);
>> }
>
> The same.
>
>>
>> /**
>> + * usb_gadget_start - start the usb gadget controller and connect to bus
>> + * @gadget: the gadget device to start
>> + *
>> + * This is external API for use by OTG core.
>> + *
>> + * Start the usb device controller and connect to bus (enable pull).
>> + */
>> +static int usb_gadget_start(struct usb_gadget *gadget)
>> +{
>> + int ret;
>> + struct usb_udc *udc = NULL;
>> +
>> + dev_dbg(&gadget->dev, "%s\n", __func__);
>
> The same
>
>> + mutex_lock(&udc_lock);
>> + list_for_each_entry(udc, &udc_list, list)
>> + if (udc->gadget == gadget)
>> + goto found;
>> +
>> + dev_err(gadget->dev.parent, "%s: gadget not registered.\n",
>> + __func__);
>> + mutex_unlock(&udc_lock);
>> + return -EINVAL;
>> +
>> +found:
>> + ret = usb_gadget_udc_start(udc);
>> + if (ret)
>> + dev_err(&udc->dev, "USB Device Controller didn't start: %d\n",
>> + ret);
>> + else
>> + usb_udc_connect_control(udc);
>> +
>> + mutex_unlock(&udc_lock);
>> +
>> + return ret;
>> +}
>> +
>> +/**
>> + * usb_gadget_stop - disconnect from bus and stop the usb gadget
>> + * @gadget: The gadget device we want to stop
>> + *
>> + * This is external API for use by OTG core.
>> + *
>> + * Disconnect from the bus (disable pull) and stop the
>> + * gadget controller.
>> + */
>> +static int usb_gadget_stop(struct usb_gadget *gadget)
>> +{
>> + struct usb_udc *udc = NULL;
>> +
>> + dev_dbg(&gadget->dev, "%s\n", __func__);
>
> The same
>
>> + 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;
>
> The above finding gadget code is the same with usb_gadget_start, can we
> use one common API to instead of it?

Sure.
>
>> +
>> +found:
>> + usb_gadget_disconnect(udc->gadget);
>> + udc->driver->disconnect(udc->gadget);
>> + usb_gadget_udc_stop(udc);
>> + mutex_unlock(&udc_lock);
>> +
>> + return 0;
>> +}
>> +
>> +/**
>> * usb_udc_release - release the usb_udc struct
>> * @dev: the dev member within usb_udc
>> *
>> @@ -486,6 +562,48 @@ int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget)
>> }
>> EXPORT_SYMBOL_GPL(usb_add_gadget_udc);
>>
>> +/**
>> + * usb_otg_add_gadget_udc - adds a new gadget to the udc class driver list
>> + * @parent: the parent device to this udc. Usually the controller
>> + * driver's device.
>> + * @gadget: the gadget to be added to the list
>> + * @otg_dev: the OTG controller device
>> + *
>> + * If otg_dev is NULL then device tree node is checked
>> + * for OTG controller via the otg-controller property.
>> + * Returns zero on success, negative errno otherwise.
>> + */
>> +int usb_otg_add_gadget_udc(struct device *parent, struct usb_gadget *gadget,
>> + struct device *otg_dev)
>> +{
>> + if (!otg_dev) {
>> + struct device_node *np;
>> + struct platform_device *pdev;
>> +
>> + np = of_parse_phandle(parent->of_node, "otg-controller", 0);
>> + if (!np) {
>> + dev_err(parent,
>> + "otg_dev is NULL or no otg-controller property in DT\n");
>> + return -EINVAL;
>> + }
>> +
>> + pdev = of_find_device_by_node(np);
>> + of_node_put(np);
>> + if (!pdev) {
>> + dev_err(parent, "couldn't get otg-controller device\n");
>> + return -ENODEV;
>> + }
>> +
>> + gadget->otg_dev = &pdev->dev;
>> + } else {
>> + gadget->otg_dev = otg_dev;
>> + }
>
> The above the code is duplicated with hcd's. Can we do something common
> at usb-otg.c, eg define an API get_usb_otg_dev(struct usb_gadget *gadget, struct
> usb_hcd *hcd), in this API we can try to get otg_dev for both gadget and
> hcd, and we can call it at controller driver during register otg.
>
Good point. I'll address it in v7.

cheers,
-roger

2016-04-20 06:55:09

by Roger Quadros

[permalink] [raw]
Subject: Re: [PATCH v6 07/12] usb: otg: add OTG/dual-role core

On 18/04/16 05:09, Peter Chen wrote:
> On Fri, Apr 15, 2016 at 02:00:46PM +0300, Roger Quadros wrote:
>> On 15/04/16 12:25, Peter Chen wrote:
>>> On Tue, Apr 05, 2016 at 05:05:12PM +0300, Roger Quadros wrote:
>>>> + * usb_otg_register() - Register the OTG/dual-role device to OTG core
>>>> + * @dev: OTG/dual-role controller device.
>>>> + * @config: OTG configuration.
>>>> + *
>>>> + * Registers the OTG/dual-role controller device with the USB OTG core.
>>>> + *
>>>> + * Return: struct usb_otg * if success, ERR_PTR() if error.
>>>> + */
>>>> +struct usb_otg *usb_otg_register(struct device *dev,
>>>> + struct usb_otg_config *config)
>>>> +{
>>>> + struct usb_otg *otg;
>>>> + struct otg_wait_data *wait;
>>>> + int ret = 0;
>>>> +
>>>> + if (!dev || !config || !config->fsm_ops)
>>>> + return ERR_PTR(-EINVAL);
>>>> +
>>>> + /* already in list? */
>>>> + mutex_lock(&otg_list_mutex);
>>>> + if (usb_otg_get_data(dev)) {
>>>> + dev_err(dev, "otg: %s: device already in otg list\n",
>>>> + __func__);
>>>> + ret = -EINVAL;
>>>> + goto unlock;
>>>> + }
>>>> +
>>>> + /* allocate and add to list */
>>>> + otg = kzalloc(sizeof(*otg), GFP_KERNEL);
>>>> + if (!otg) {
>>>> + ret = -ENOMEM;
>>>> + goto unlock;
>>>> + }
>>>> +
>>>> + otg->dev = dev;
>>>> + otg->caps = config->otg_caps;
>>>> +
>>>> + if ((otg->caps->hnp_support || otg->caps->srp_support ||
>>>> + otg->caps->adp_support) && !config->otg_work)
>>>> + dev_info(dev, "otg: limiting to dual-role\n");
>>>
>>> What does above mean? Customized otg_work item may be dual-role,
>>> may be full otg.
>>
>> I'm checking for !config->otg_work so we're sure of using the
>> default dual-role only work function.
>>
>
> I see. But whether it is dual-role or fully otg should depend on
> otg->caps, the work item may be different according to design.
> Besides, your code seems to depend on one of the otg capabilities
> for dual-role.
>
Which capability? Id/vbus status?

Are you OK with this patch?

cheers,
-roger

2016-04-20 07:03:13

by Roger Quadros

[permalink] [raw]
Subject: Re: [PATCH v6 07/12] usb: otg: add OTG/dual-role core

On 19/04/16 11:06, Peter Chen wrote:
> On Tue, Apr 05, 2016 at 05:05:12PM +0300, Roger Quadros wrote:
>> +/**
>> + * usb_otg_start_host - start/stop the host controller
>> + * @otg: usb_otg instance
>> + * @on: true to start, false to stop
>> + *
>> + * Start/stop the USB host controller. This function is meant
>> + * for use by the OTG controller driver.
>> + */
>> +int usb_otg_start_host(struct usb_otg *otg, int on)
>> +{
>> + struct otg_hcd_ops *hcd_ops = otg->hcd_ops;
>> +
>> + dev_dbg(otg->dev, "otg: %s %d\n", __func__, on);
>> + if (!otg->host) {
>> + WARN_ONCE(1, "otg: fsm running without host\n");
>> + return 0;
>> + }
>> +
>> + if (on) {
>> + if (otg->flags & OTG_FLAG_HOST_RUNNING)
>> + return 0;
>> +
>> + otg->flags |= OTG_FLAG_HOST_RUNNING;
>> +
>> + /* start host */
>> + hcd_ops->add(otg->primary_hcd.hcd, otg->primary_hcd.irqnum,
>> + otg->primary_hcd.irqflags);
>> + if (otg->shared_hcd.hcd) {
>> + hcd_ops->add(otg->shared_hcd.hcd,
>> + otg->shared_hcd.irqnum,
>> + otg->shared_hcd.irqflags);
>> + }
>
> Check the return value please.

And what should we do on failure?
Even if things fail, they could potentially start working on next
remove/add iteration so I didn't bother checking return values.

>
>> + } else {
>> + if (!(otg->flags & OTG_FLAG_HOST_RUNNING))
>> + return 0;
>> +
>> + otg->flags &= ~OTG_FLAG_HOST_RUNNING;
>> +
>> + /* stop host */
>> + if (otg->shared_hcd.hcd)
>> + hcd_ops->remove(otg->shared_hcd.hcd);
>> +
>> + hcd_ops->remove(otg->primary_hcd.hcd);
>> + }
>> +
>> + return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(usb_otg_start_host);
>> +
>> +/**
>> + * usb_otg_start_gadget - start/stop the gadget controller
>> + * @otg: usb_otg instance
>> + * @on: true to start, false to stop
>> + *
>> + * Start/stop the USB gadget controller. This function is meant
>> + * for use by the OTG controller driver.
>> + */
>> +int usb_otg_start_gadget(struct usb_otg *otg, int on)
>> +{
>> + struct usb_gadget *gadget = otg->gadget;
>> +
>> + dev_dbg(otg->dev, "otg: %s %d\n", __func__, on);
>> + if (!gadget) {
>> + WARN_ONCE(1, "otg: fsm running without gadget\n");
>> + return 0;
>> + }
>> +
>> + if (on) {
>> + if (otg->flags & OTG_FLAG_GADGET_RUNNING)
>> + return 0;
>> +
>> + otg->flags |= OTG_FLAG_GADGET_RUNNING;
>> + otg->gadget_ops->start(otg->gadget);
>
> ditto
>
>> + } else {
>> + if (!(otg->flags & OTG_FLAG_GADGET_RUNNING))
>> + return 0;
>> +
>> + otg->flags &= ~OTG_FLAG_GADGET_RUNNING;
>> + otg->gadget_ops->stop(otg->gadget);
>> + }
>> +
>> + return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(usb_otg_start_gadget);
>> +
>> +/**
>> + * Change USB protocol when there is a protocol change.
>> + * fsm->lock must be held.
>> + */
>> +static int drd_set_protocol(struct otg_fsm *fsm, int protocol)
>> +{
>> + struct usb_otg *otg = container_of(fsm, struct usb_otg, fsm);
>> + int ret = 0;
>> +
>> + if (fsm->protocol != protocol) {
>> + dev_dbg(otg->dev, "otg: changing role fsm->protocol= %d; new protocol= %d\n",
>> + fsm->protocol, protocol);
>> + /* stop old protocol */
>> + if (fsm->protocol == PROTO_HOST)
>> + ret = otg_start_host(otg, 0);
>> + else if (fsm->protocol == PROTO_GADGET)
>> + 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);
>> + if (ret)
>> + return ret;
>> +
>> + fsm->protocol = protocol;
>> + return 0;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +/**
>> + * Called when entering a DRD state.
>> + * fsm->lock must be held.
>> + */
>> +static void drd_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state)
>> +{
>> + struct usb_otg *otg = container_of(fsm, struct usb_otg, fsm);
>> +
>> + if (otg->state == new_state)
>> + return;
>> +
>> + fsm->state_changed = 1;
>> + dev_dbg(otg->dev, "otg: set state: %s\n",
>> + usb_otg_state_string(new_state));
>> + switch (new_state) {
>> + case OTG_STATE_B_IDLE:
>> + drd_set_protocol(fsm, PROTO_UNDEF);
>> + otg_drv_vbus(otg, 0);
>> + break;
>> + case OTG_STATE_B_PERIPHERAL:
>> + drd_set_protocol(fsm, PROTO_GADGET);
>> + otg_drv_vbus(otg, 0);
>> + break;
>> + case OTG_STATE_A_HOST:
>> + drd_set_protocol(fsm, PROTO_HOST);
>> + otg_drv_vbus(otg, 1);
>> + break;
>> + 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(otg->dev, "%s: otg: invalid state: %s\n",
>> + __func__, usb_otg_state_string(new_state));
>> + break;
>> + }
>> +
>> + otg->state = new_state;
>> +}
>> +
>> +/**
>> + * DRD state change judgement
>> + *
>> + * For DRD we're only interested in some of the OTG states
>> + * i.e. OTG_STATE_B_IDLE: both peripheral and host are stopped
>> + * OTG_STATE_B_PERIPHERAL: peripheral active
>> + * OTG_STATE_A_HOST: host active
>> + * we're only interested in the following inputs
>> + * fsm->id, fsm->b_sess_vld
>> + */
>> +int drd_statemachine(struct usb_otg *otg)
>> +{
>> + struct otg_fsm *fsm = &otg->fsm;
>> + enum usb_otg_state state;
>> + int ret;
>> +
>> + mutex_lock(&fsm->lock);
>> +
>> + fsm->state_changed = 0;
>> + state = otg->state;
>> +
>> + switch (state) {
>> + case OTG_STATE_UNDEFINED:
>> + if (!fsm->id)
>> + drd_set_state(fsm, OTG_STATE_A_HOST);
>> + else if (fsm->id && fsm->b_sess_vld)
>> + drd_set_state(fsm, OTG_STATE_B_PERIPHERAL);
>> + else
>> + drd_set_state(fsm, OTG_STATE_B_IDLE);
>> + break;
>> + case OTG_STATE_B_IDLE:
>> + if (!fsm->id)
>> + drd_set_state(fsm, OTG_STATE_A_HOST);
>> + else if (fsm->b_sess_vld)
>> + drd_set_state(fsm, OTG_STATE_B_PERIPHERAL);
>> + break;
>> + case OTG_STATE_B_PERIPHERAL:
>> + if (!fsm->id)
>> + drd_set_state(fsm, OTG_STATE_A_HOST);
>> + else if (!fsm->b_sess_vld)
>> + drd_set_state(fsm, OTG_STATE_B_IDLE);
>> + break;
>> + case OTG_STATE_A_HOST:
>> + if (fsm->id && fsm->b_sess_vld)
>> + drd_set_state(fsm, OTG_STATE_B_PERIPHERAL);
>> + else if (fsm->id && !fsm->b_sess_vld)
>> + drd_set_state(fsm, OTG_STATE_B_IDLE);
>> + break;
>> +
>> + /* 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(otg->dev, "%s: otg: invalid usb-drd state: %s\n",
>> + __func__, usb_otg_state_string(state));
>> + drd_set_state(fsm, OTG_STATE_UNDEFINED);
>> + 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);
>> +
>> +/**
>> + * OTG FSM/DRD work function
>> + */
>> +static void usb_otg_work(struct work_struct *work)
>> +{
>> + struct usb_otg *otg = container_of(work, struct usb_otg, work);
>> +
>> + pm_runtime_get_sync(otg->dev);
>> + drd_statemachine(otg);
>> + pm_runtime_put_sync(otg->dev);
>> +}
>> +
>> +/**
>> + * usb_otg_register() - Register the OTG/dual-role device to OTG core
>> + * @dev: OTG/dual-role controller device.
>> + * @config: OTG configuration.
>> + *
>> + * Registers the OTG/dual-role controller device with the USB OTG core.
>> + *
>> + * Return: struct usb_otg * if success, ERR_PTR() if error.
>> + */
>> +struct usb_otg *usb_otg_register(struct device *dev,
>> + struct usb_otg_config *config)
>> +{
>> + struct usb_otg *otg;
>> + struct otg_wait_data *wait;
>> + int ret = 0;
>> +
>> + if (!dev || !config || !config->fsm_ops)
>> + return ERR_PTR(-EINVAL);
>> +
>> + /* already in list? */
>> + mutex_lock(&otg_list_mutex);
>> + if (usb_otg_get_data(dev)) {
>> + dev_err(dev, "otg: %s: device already in otg list\n",
>> + __func__);
>> + ret = -EINVAL;
>
> Using -EEXIST please

OK.
>
>> + goto unlock;
>> + }
>> +
>> + /* allocate and add to list */
>> + otg = kzalloc(sizeof(*otg), GFP_KERNEL);
>> + if (!otg) {
>> + ret = -ENOMEM;
>> + goto unlock;
>> + }
>> +
>> + otg->dev = dev;
>> + otg->caps = config->otg_caps;
>> +
>> + if ((otg->caps->hnp_support || otg->caps->srp_support ||
>> + otg->caps->adp_support) && !config->otg_work)
>> + dev_info(dev, "otg: limiting to dual-role\n");
>> +
>> + if (config->otg_work) /* custom otg_work ? */
>> + INIT_WORK(&otg->work, config->otg_work);
>> + else
>> + INIT_WORK(&otg->work, usb_otg_work);
>> +
>> + otg->wq = create_singlethread_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);
>> +
>> + /* were we in wait list? */
>> + mutex_lock(&wait_list_mutex);
>> + wait = usb_otg_get_wait(dev);
>> + mutex_unlock(&wait_list_mutex);
>> + if (wait) {
>> + /* register pending host/gadget and flush from list */
>> + usb_otg_flush_wait(dev);
>> + }
>> +
>> + return otg;
>> +
>> +err_wq:
>> + kfree(otg);
>> +unlock:
>> + mutex_unlock(&otg_list_mutex);
>> + return ERR_PTR(ret);
>> +}
>> +EXPORT_SYMBOL_GPL(usb_otg_register);
>> +
>> +/**
>> + * usb_otg_unregister() - Unregister the OTG/dual-role device from USB OTG core
>> + * @dev: OTG controller device.
>> + *
>> + * Unregisters the OTG/dual-role controller device from USB OTG core.
>> + * Prevents unregistering till both the associated Host and Gadget controllers
>> + * have unregistered from the OTG core.
>> + *
>> + * Return: 0 on success, error value otherwise.
>> + */
>> +int usb_otg_unregister(struct device *dev)
>> +{
>> + struct usb_otg *otg;
>> +
>> + mutex_lock(&otg_list_mutex);
>> + otg = usb_otg_get_data(dev);
>> + if (!otg) {
>> + dev_err(dev, "otg: %s: device not in otg list\n",
>> + __func__);
>> + mutex_unlock(&otg_list_mutex);
>> + return -EINVAL;
>> + }
>> +
>> + /* prevent unregister till both host & gadget have unregistered */
>> + if (otg->host || otg->gadget) {
>> + dev_err(dev, "otg: %s: host/gadget still registered\n",
>> + __func__);
>> + return -EBUSY;
>> + }
>> +
>> + /* OTG FSM is halted when host/gadget unregistered */
>> + destroy_workqueue(otg->wq);
>> +
>> + /* remove from otg list */
>> + list_del(&otg->list);
>> + kfree(otg);
>> + mutex_unlock(&otg_list_mutex);
>> +
>> + return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(usb_otg_unregister);
>> +
>> +/**
>> + * start/kick the OTG FSM if we can
>> + * fsm->lock must be held
>> + */
>> +static void usb_otg_start_fsm(struct usb_otg *otg)
>> +{
>> + struct otg_fsm *fsm = &otg->fsm;
>> +
>> + if (fsm->running)
>> + goto kick_fsm;
>> +
>> + if (!otg->host) {
>> + dev_info(otg->dev, "otg: can't start till host registers\n");
>> + return;
>> + }
>
> dev_err
>
>> +
>> + if (!otg->gadget) {
>> + dev_info(otg->dev, "otg: can't start till gadget registers\n");
>> + return;
>> + }
>
> dev_err

They are not error messages. One of them will always register first and most likely
gadget driver will not be loaded automatically.

It is an informative message to the user that USB won't work till gadget driver is
loaded.

>
>> +
>> + fsm->running = true;
>> +kick_fsm:
>> + queue_work(otg->wq, &otg->work);
>> +}
>> +
>> +/**
>> + * stop the OTG FSM. Stops Host & Gadget controllers as well.
>> + * fsm->lock must be held
>> + */
>> +static void usb_otg_stop_fsm(struct usb_otg *otg)
>> +{
>> + struct otg_fsm *fsm = &otg->fsm;
>> +
>> + if (!fsm->running)
>> + return;
>> +
>> + /* no more new events queued */
>> + fsm->running = false;
>> +
>> + flush_workqueue(otg->wq);
>> + otg->state = OTG_STATE_UNDEFINED;
>> +
>> + /* stop host/gadget immediately */
>> + if (fsm->protocol == PROTO_HOST)
>> + otg_start_host(otg, 0);
>> + else if (fsm->protocol == PROTO_GADGET)
>> + otg_start_gadget(otg, 0);
>> + fsm->protocol = PROTO_UNDEF;
>> +}
>> +
>> +/**
>> + * usb_otg_sync_inputs - Sync OTG inputs with the OTG state machine
>> + * @fsm: OTG FSM instance
>> + *
>> + * Used by the OTG driver to update the inputs to the OTG
>> + * state machine.
>> + *
>> + * Can be called in IRQ context.
>> + */
>> +void usb_otg_sync_inputs(struct usb_otg *otg)
>> +{
>> + /* Don't kick FSM till it has started */
>> + if (!otg->fsm.running)
>> + return;
>> +
>> + /* Kick FSM */
>> + queue_work(otg->wq, &otg->work);
>> +}
>> +EXPORT_SYMBOL_GPL(usb_otg_sync_inputs);
>> +
>> +/**
>> + * usb_otg_kick_fsm - Kick the OTG state machine
>> + * @otg_dev: OTG controller device
>> + *
>> + * Used by USB host/device stack to sync OTG related
>> + * events to the OTG state machine.
>> + * e.g. change in host_bus->b_hnp_enable, gadget->b_hnp_enable
>> + *
>> + * Returns: 0 on success, error value otherwise.
>> + */
>> +int usb_otg_kick_fsm(struct device *otg_dev)
>> +{
>> + struct usb_otg *otg;
>> +
>> + mutex_lock(&otg_list_mutex);
>> + otg = usb_otg_get_data(otg_dev);
>> + mutex_unlock(&otg_list_mutex);
>> + if (!otg) {
>> + dev_dbg(otg_dev, "otg: %s: invalid otg device\n",
>> + __func__);
>> + return -ENODEV;
>> + }
>> +
>> + usb_otg_sync_inputs(otg);
>> +
>> + return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(usb_otg_kick_fsm);
>> +
>> +/**
>> + * usb_otg_register_hcd - Register the host controller to OTG core
>> + * @hcd: host controller device
>> + * @irqnum: interrupt number
>> + * @irqflags: interrupt flags
>> + * @ops: HCD ops to interface with the HCD
>> + *
>> + * This is used by the USB Host stack to register the host controller
>> + * to the OTG core. Host controller must not be started by the
>> + * caller as it is left upto the OTG state machine to do so.
>
> %s/upto/up to

OK.
>
>> + * hcd->otg_dev must contain the related otg controller device.
>> + *
>> + * Returns: 0 on success, error value otherwise.
>> + */
>> +int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
>> + unsigned long irqflags, struct otg_hcd_ops *ops)
>> +{
>> + struct usb_otg *otg;
>> + struct device *hcd_dev = hcd->self.controller;
>> + struct device *otg_dev = hcd->otg_dev;
>> +
>> + if (!otg_dev)
>> + return -EINVAL;
>> +
>> + /* we're otg but otg controller might not yet be registered */
>> + mutex_lock(&otg_list_mutex);
>> + otg = usb_otg_get_data(otg_dev);
>> + mutex_unlock(&otg_list_mutex);
>> + if (!otg) {
>> + dev_dbg(hcd_dev,
>> + "otg: controller not yet registered. waiting..\n");
>> + /*
>> + * otg controller might register later. Put the hcd in
>> + * wait list and call us back when ready
>> + */
>> + if (usb_otg_hcd_wait_add(otg_dev, hcd, irqnum, irqflags, ops)) {
>> + dev_err(hcd_dev, "otg: failed to add hcd to wait list\n");
>> + return -EINVAL;
>> + }
>> +
>> + return 0;
>> + }
>> +
>> + /* 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) {
>> + /* are we in wait list? */
>> + if (!usb_otg_hcd_wait_remove(hcd))
>> + return 0;
>> +
>> + dev_dbg(hcd_dev, "otg: host wasn't registered with otg\n");
>> + return -EINVAL;
>> + }
>> +
>> + mutex_lock(&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 {
>> + dev_err(otg_dev, "otg: host %s wasn't registered with otg\n",
>> + dev_name(hcd_dev));
>> + mutex_unlock(&otg->fsm.lock);
>> + return -EINVAL;
>> + }
>> +
>> + /* stop FSM & Host */
>> + usb_otg_stop_fsm(otg);
>> + otg->host = NULL;
>> +
>> + mutex_unlock(&otg->fsm.lock);
>> +
>> + return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(usb_otg_unregister_hcd);
>> +
>> +/**
>> + * usb_otg_register_gadget - Register the gadget controller to OTG core
>> + * @gadget: gadget controller
>> + *
>> + * This is used by the USB gadget stack to register the gadget controller
>> + * to the OTG core. Gadget controller must not be started by the
>> + * caller as it is left upto the OTG state machine to do so.
>
> %s/upto/up to
>
>> + *
>> + * Gadget core must call this only when all resources required for
>> + * gadget controller to run are available.
>> + * i.e. gadget function driver is available.
>> + *
>> + * Returns: 0 on success, error value otherwise.
>> + */
>> +int usb_otg_register_gadget(struct usb_gadget *gadget,
>> + struct otg_gadget_ops *ops)
>> +{
>> + struct usb_otg *otg;
>> + struct device *gadget_dev = &gadget->dev;
>> + struct device *otg_dev = gadget->otg_dev;
>> +
>> + if (!otg_dev)
>> + return -EINVAL; /* we're definitely not OTG */
>> +
>> + /* we're otg but otg controller might not yet be registered */
>> + mutex_lock(&otg_list_mutex);
>> + otg = usb_otg_get_data(otg_dev);
>> + mutex_unlock(&otg_list_mutex);
>> + if (!otg) {
>> + dev_dbg(gadget_dev,
>> + "otg: controller not yet registered. waiting..\n");
>> + /*
>> + * otg controller might register later. Put the gadget in
>> + * wait list and call us back when ready
>> + */
>> + if (usb_otg_gadget_wait_add(otg_dev, gadget, ops)) {
>> + dev_err(gadget_dev,
>> + "otg: failed to add to gadget to wait list\n");
>> + return -EINVAL;
>> + }
>> +
>> + return 0;
>> + }
>> +
>> + 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));
>> +
>> + /* start FSM */
>> + usb_otg_start_fsm(otg);
>> + mutex_unlock(&otg->fsm.lock);
>> +
>> + return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(usb_otg_register_gadget);
>> +
>> +/**
>> + * usb_otg_unregister_gadget - Unregister the gadget controller from OTG core
>> + * @gadget: gadget controller
>> + *
>> + * This is used by the USB gadget stack to unregister the gadget controller
>> + * from the OTG core. Ensures that gadget controller is halted
>> + * on successful return.
>> + *
>> + * Returns: 0 on success, error value otherwise.
>> + */
>> +int usb_otg_unregister_gadget(struct usb_gadget *gadget)
>> +{
>> + struct usb_otg *otg;
>> + struct device *gadget_dev = &gadget->dev;
>> + struct device *otg_dev = gadget->otg_dev;
>> +
>> + if (!otg_dev)
>> + return -EINVAL;
>> +
>> + mutex_lock(&otg_list_mutex);
>> + otg = usb_otg_get_data(otg_dev);
>> + mutex_unlock(&otg_list_mutex);
>> + if (!otg) {
>> + /* are we in wait list? */
>> + if (!usb_otg_gadget_wait_remove(gadget))
>> + return 0;
>> +
>> + dev_dbg(gadget_dev, "otg: gadget wasn't registered with otg\n");
>> + return -EINVAL;
>> + }
>> +
>> + mutex_lock(&otg->fsm.lock);
>> + if (otg->gadget != gadget) {
>> + dev_err(otg_dev, "otg: gadget %s wasn't registered with otg\n",
>> + dev_name(&gadget->dev));
>> + mutex_unlock(&otg->fsm.lock);
>> + return -EINVAL;
>> + }
>> +
>> + /* Stop FSM & gadget */
>> + 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);
>> diff --git a/drivers/usb/common/usb-otg.h b/drivers/usb/common/usb-otg.h
>> new file mode 100644
>> index 0000000..2bf3fbf
>> --- /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) 2016 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)
>> +
>
> Since this set is mainly for DRD, these FSM stuffs aren't needed.
> Besides, there is already an otg-fsm.h at include/linux/usb/, and
> there are both otg and otg-fsm source and headers, we may let these
> timing thing belong to otg-fsm.

You are right. I'll remove them from this series.

>
>> +#endif /* __DRIVERS_USB_COMMON_USB_OTG_H */
>> diff --git a/drivers/usb/core/Kconfig b/drivers/usb/core/Kconfig
>> index ae228d0..b468a9f 100644
>> --- a/drivers/usb/core/Kconfig
>> +++ b/drivers/usb/core/Kconfig
>> @@ -42,7 +42,7 @@ config USB_DYNAMIC_MINORS
>> If you are unsure about this, say N here.
>>
>> config USB_OTG
>> - bool "OTG support"
>> + bool "OTG/Dual-role support"
>> depends on PM
>> default n
>> help
>> diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h
>> index 8c0ae64..1878ae1 100644
>> --- a/include/linux/usb/gadget.h
>> +++ b/include/linux/usb/gadget.h
>> @@ -583,6 +583,7 @@ struct usb_gadget_ops {
>> * @out_epnum: last used out ep number
>> * @in_epnum: last used in ep number
>> * @otg_caps: OTG capabilities of this gadget.
>> + * @otg_dev: OTG controller device, if needs to be used with OTG core.
>> * @sg_supported: true if we can handle scatter-gather
>> * @is_otg: True if the USB device port uses a Mini-AB jack, so that the
>> * gadget driver must provide a USB OTG descriptor.
>> @@ -639,6 +640,7 @@ struct usb_gadget {
>> unsigned out_epnum;
>> unsigned in_epnum;
>> struct usb_otg_caps *otg_caps;
>> + struct device *otg_dev;
>>
>> unsigned sg_supported:1;
>> unsigned is_otg:1;
>> diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h
>> index 861ccaa..2017cd4 100644
>> --- a/include/linux/usb/hcd.h
>> +++ b/include/linux/usb/hcd.h
>> @@ -184,6 +184,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 36f0cf9..ba6755c 100644
>> --- a/include/linux/usb/otg-fsm.h
>> +++ b/include/linux/usb/otg-fsm.h
>> @@ -61,6 +61,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
>> @@ -133,6 +138,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 */
>> @@ -188,6 +194,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..b094352 100644
>> --- a/include/linux/usb/otg.h
>> +++ b/include/linux/usb/otg.h
>> @@ -10,10 +10,55 @@
>> #define __LINUX_USB_OTG_H
>>
>> #include <linux/phy/phy.h>
>> -#include <linux/usb/phy.h>
>> -#include <linux/usb/otg-fsm.h>
>> +#include <linux/device.h>
>> +#include <linux/hrtimer.h>
>> +#include <linux/ktime.h>
>
> Does above two headers are really needed?

Not any more.

>
>> +#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>
>
> Does above header is really needed?

Probably not. I'll remove it and check if it builds.
>
>>
>> +/**
>> + * struct otg_hcd - host controller state and interface
>> + *
>> + * @hcd: host controller
>> + * @irqnum: irq number
>> + * @irqflags: irq flags
>> + * @ops: otg to host controller interface
>> + * @ops: otg to host controller interface
>
> Duplicated line
>
OK.

--
cheers,
-roger

2016-04-20 07:03:57

by Roger Quadros

[permalink] [raw]
Subject: Re: [PATCH v6 07/12] usb: otg: add OTG/dual-role core

On 20/04/16 08:08, Yoshihiro Shimoda wrote:
> Hi,
>
>> From: Peter Chen
>> Sent: Tuesday, April 19, 2016 6:18 PM
>>
>> On Fri, Apr 15, 2016 at 10:03:16AM +0000, Yoshihiro Shimoda wrote:
>>> Hi,
>>>
>>>> From: Yoshihiro Shimoda
>>>> Sent: Friday, April 15, 2016 6:59 PM
>>>>
>>>> Hi,
>>>>
>>>>> From: Roger Quadros
>>>>> Sent: Thursday, April 14, 2016 8:32 PM
>>>>>
>>>>> On 14/04/16 14:15, Yoshihiro Shimoda wrote:
>>>>>> Hi,
>>>>>>
>>>> < snip >
>>>>>>>> @@ -865,7 +867,8 @@ 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->caps->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 */
>>>>>>>>
>>>>>>>
>>>>>>> These changes look good to me. Thanks.
>>>>>>
>>>>>> Thank you for the comment.
>>>>>> If we change the "needs_companion" place to the otg_config,
>>>>>> do we need to add a flag into the otg, instead of otg->caps?
>>>>>
>>>>> Yes we can add a flag in struct usb_otg.
>>>>
>>>> Thank you for the comment.
>>>>
>>>> I made a fixed patch.
>>>> So, should I send this patch to ML after you sent v7 patches?
>>>> Or, would you apply this patch before you send v7 patches?
>>>
>>> Oops, I sent this email without my patch...
>>>
>>> ---
>>> Subject: [PATCH] usb: otg: add hcd companion support
>>>
>>> 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 <[email protected]>
>>> ---
>>> Documentation/devicetree/bindings/usb/generic.txt | 3 +++
>>> drivers/usb/common/usb-otg.c | 17 +++++++++++++----
>>> include/linux/usb/otg.h | 7 ++++++-
>>> 3 files changed, 22 insertions(+), 5 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 41e762a..83c8c96 100644
>>> --- a/drivers/usb/common/usb-otg.c
>>> +++ b/drivers/usb/common/usb-otg.c
>>> @@ -20,6 +20,7 @@
>>> #include <linux/list.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>
>>> @@ -600,6 +601,10 @@ struct usb_otg *usb_otg_register(struct device *dev,
>>> else
>>> INIT_WORK(&otg->work, usb_otg_work);
>>>
>>> + if (of_find_property(dev->of_node, "hcd-needs-companion", NULL) ||
>>> + config->hcd_needs_companion) /* needs comanion ? */
>>
>> %s/comanion/companion
>
> Thank you for pointing it out!
>
> Roger, would you fix this in your v7 patch set?

Yes, I'll fix it locally. You don't need to post it again.

--
cheers,
-roger

>
>> I have a little puzzled with companion controller and shared hcd, let me
>> post a topic for it.
>
> I looked at the email thread.
> It is very useful information to me! :)
>
> Best regards,
> Yoshihiro Shimoda
>
>> Peter
>>
>>> + otg->flags |= OTG_FLAG_HCD_NEEDS_COMPANION;
>>> +
>>> otg->wq = create_singlethread_workqueue("usb_otg");
>>> if (!otg->wq) {
>>> dev_err(dev, "otg: %s: can't create workqueue\n",
>>> @@ -823,13 +828,15 @@ 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");
>>> goto err;
>>> @@ -865,7 +872,9 @@ 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 */
>>>
>>> diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h
>>> index b094352..6f4ca77 100644
>>> --- a/include/linux/usb/otg.h
>>> +++ b/include/linux/usb/otg.h
>>> @@ -57,7 +57,8 @@ struct otg_hcd {
>>> * @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;
>>> @@ -84,6 +85,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 -----------------------------*/
>>> @@ -125,11 +127,14 @@ struct usb_otg_caps {
>>> * @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);
>>> --
>>> 1.9.1
>>>
>>> --
>>> To unsubscribe from this list: send the line "unsubscribe linux-usb" in
>>> the body of a message to [email protected]
>>> More majordomo info at http://vger.kernel.org/majordomo-info.html
>>
>> --
>>
>> Best Regards,
>> Peter Chen

2016-04-20 08:13:00

by Roger Quadros

[permalink] [raw]
Subject: Re: [PATCH v6 06/12] usb: otg: get rid of CONFIG_USB_OTG_FSM in favour of CONFIG_USB_OTG

On 18/04/16 11:05, Peter Chen wrote:
> On Tue, Apr 05, 2016 at 05:05:11PM +0300, Roger Quadros wrote:
>> Let's use CONFIG_USB_OTG as a single config option to enable
>> USB OTG and the OTG FSM. This makes things a lot less confusing.
>>
>> Update all users of CONFIG_USB_OTG_FSM to CONFIG_USB_OTG.
>>
>> Signed-off-by: Roger Quadros <[email protected]>
>> ---
>> Documentation/usb/chipidea.txt | 2 +-
>> drivers/usb/Makefile | 1 +
>> drivers/usb/chipidea/Makefile | 2 +-
>> drivers/usb/chipidea/ci.h | 2 +-
>> drivers/usb/chipidea/otg_fsm.h | 2 +-
>> drivers/usb/common/Makefile | 3 ++-
>> drivers/usb/core/Kconfig | 8 --------
>> drivers/usb/phy/Kconfig | 2 +-
>> 8 files changed, 8 insertions(+), 14 deletions(-)
>>
>> diff --git a/Documentation/usb/chipidea.txt b/Documentation/usb/chipidea.txt
>> index 678741b..3b1f263 100644
>> --- a/Documentation/usb/chipidea.txt
>> +++ b/Documentation/usb/chipidea.txt
>> @@ -5,7 +5,7 @@ with 2 Freescale i.MX6Q sabre SD boards.
>>
>> 1.1 How to enable OTG FSM in menuconfig
>> ---------------------------------------
>> -Select CONFIG_USB_OTG_FSM, rebuild kernel Image and modules.
>> +Select CONFIG_USB_OTG, rebuild kernel Image and modules.
>> If you want to check some internal variables for otg fsm,
>> mount debugfs, there are 2 files which can show otg fsm
>> variables and some controller registers value:
>> diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
>> index dca7856..16a5b55 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) += common/
>
> Why we need to add above line?

It is not needed as USB_OTG can't be set without USB_COMMON.
I'll remove it.

cheers,
-roger

>
> Peter
>>
>> obj-$(CONFIG_USBIP_CORE) += usbip/
>> diff --git a/drivers/usb/chipidea/Makefile b/drivers/usb/chipidea/Makefile
>> index 518e445..45aa24d 100644
>> --- a/drivers/usb/chipidea/Makefile
>> +++ b/drivers/usb/chipidea/Makefile
>> @@ -3,7 +3,7 @@ obj-$(CONFIG_USB_CHIPIDEA) += ci_hdrc.o
>> ci_hdrc-y := core.o otg.o debug.o
>> ci_hdrc-$(CONFIG_USB_CHIPIDEA_UDC) += udc.o
>> ci_hdrc-$(CONFIG_USB_CHIPIDEA_HOST) += host.o
>> -ci_hdrc-$(CONFIG_USB_OTG_FSM) += otg_fsm.o
>> +ci_hdrc-$(CONFIG_USB_OTG) += otg_fsm.o
>>
>> # Glue/Bridge layers go here
>>
>> diff --git a/drivers/usb/chipidea/ci.h b/drivers/usb/chipidea/ci.h
>> index c523975..1a32b8c 100644
>> --- a/drivers/usb/chipidea/ci.h
>> +++ b/drivers/usb/chipidea/ci.h
>> @@ -406,7 +406,7 @@ static inline u32 hw_test_and_write(struct ci_hdrc *ci, enum ci_hw_regs reg,
>> */
>> static inline bool ci_otg_is_fsm_mode(struct ci_hdrc *ci)
>> {
>> -#ifdef CONFIG_USB_OTG_FSM
>> +#ifdef CONFIG_USB_OTG
>> struct usb_otg_caps *otg_caps = &ci->platdata->ci_otg_caps;
>>
>> return ci->is_otg && ci->roles[CI_ROLE_HOST] &&
>> diff --git a/drivers/usb/chipidea/otg_fsm.h b/drivers/usb/chipidea/otg_fsm.h
>> index 6366fe3..2d451bb 100644
>> --- a/drivers/usb/chipidea/otg_fsm.h
>> +++ b/drivers/usb/chipidea/otg_fsm.h
>> @@ -64,7 +64,7 @@
>>
>> #define TB_AIDL_BDIS (20) /* 4ms ~ 150ms, section 5.2.1 */
>>
>> -#if IS_ENABLED(CONFIG_USB_OTG_FSM)
>> +#if IS_ENABLED(CONFIG_USB_OTG)
>>
>> int ci_hdrc_otg_fsm_init(struct ci_hdrc *ci);
>> int ci_otg_fsm_work(struct ci_hdrc *ci);
>> diff --git a/drivers/usb/common/Makefile b/drivers/usb/common/Makefile
>> index 6bbb3ec..f8f2c88 100644
>> --- a/drivers/usb/common/Makefile
>> +++ b/drivers/usb/common/Makefile
>> @@ -6,5 +6,6 @@ obj-$(CONFIG_USB_COMMON) += usb-common.o
>> usb-common-y += common.o
>> usb-common-$(CONFIG_USB_LED_TRIG) += led.o
>>
>> -obj-$(CONFIG_USB_OTG_FSM) += usb-otg-fsm.o
>> obj-$(CONFIG_USB_ULPI_BUS) += ulpi.o
>> +usbotg-y := usb-otg-fsm.o
>> +obj-$(CONFIG_USB_OTG) += usbotg.o
>> diff --git a/drivers/usb/core/Kconfig b/drivers/usb/core/Kconfig
>> index dd28010..ae228d0 100644
>> --- a/drivers/usb/core/Kconfig
>> +++ b/drivers/usb/core/Kconfig
>> @@ -75,14 +75,6 @@ config USB_OTG_BLACKLIST_HUB
>> and software costs by not supporting external hubs. So
>> are "Embedded Hosts" that don't offer OTG support.
>>
>> -config USB_OTG_FSM
>> - tristate "USB 2.0 OTG FSM implementation"
>> - depends on USB && USB_OTG
>> - select USB_PHY
>> - help
>> - Implements OTG Finite State Machine as specified in On-The-Go
>> - and Embedded Host Supplement to the USB Revision 2.0 Specification.
>> -
>> config USB_ULPI_BUS
>> tristate "USB ULPI PHY interface support"
>> depends on USB_SUPPORT
>> diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig
>> index c690474..06794e2 100644
>> --- a/drivers/usb/phy/Kconfig
>> +++ b/drivers/usb/phy/Kconfig
>> @@ -20,7 +20,7 @@ config AB8500_USB
>>
>> config FSL_USB2_OTG
>> bool "Freescale USB OTG Transceiver Driver"
>> - depends on USB_EHCI_FSL && USB_FSL_USB2 && USB_OTG_FSM && PM
>> + depends on USB_EHCI_FSL && USB_FSL_USB2 && USB_OTG && PM
>> select USB_PHY
>> help
>> Enable this to support Freescale USB OTG transceiver.
>> --
>> 2.5.0
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-usb" in
>> the body of a message to [email protected]
>> More majordomo info at http://vger.kernel.org/majordomo-info.html
>

2016-04-20 08:15:48

by Roger Quadros

[permalink] [raw]
Subject: Re: [PATCH v6 01/12] usb: hcd: Initialize hcd->flags to 0

On 19/04/16 04:56, Peter Chen wrote:
> On Mon, Apr 18, 2016 at 10:11:29AM -0400, Alan Stern wrote:
>> On Mon, 18 Apr 2016, Peter Chen wrote:
>>
>>> On Wed, Apr 06, 2016 at 09:32:22AM +0300, Roger Quadros wrote:
>>>> On 06/04/16 09:09, Felipe Balbi wrote:
>>>>>
>>>>> Hi,
>>>>>
>>>>> Roger Quadros <[email protected]> writes:
>>>>>> diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
>>>>>> index 2ca2cef..6b1930d 100644
>>>>>> --- a/drivers/usb/core/hcd.c
>>>>>> +++ b/drivers/usb/core/hcd.c
>>>>>> @@ -2706,6 +2706,7 @@ int usb_add_hcd(struct usb_hcd *hcd,
>>>>>> int retval;
>>>>>> struct usb_device *rhdev;
>>>>>>
>>>>>> + hcd->flags = 0;
>>>>>
>>>>> seems like this would make more sense in usb_del_hcd() instead.
>>>>>
>>>>
>>>> OK, I'll move it there.
>>>>
>>>
>>> It depends on Alan's comments, whether only usb_add_hcd/usb_del_hcd
>>> pair can be called repeat. If Alan acks it, I have no idea for it.
>>
>> Most of the host controller drivers were not written with this in mind,
>> but I think it would be a good thing to allow. It would speed up the
>> host/device role switches.
>>
>> This might mean we need to fix up several drivers to make them work
>> correctly in an OTG environment. It should be possible to do this. Is
>> there any particular reason why it would be difficult for Chipidea?
>>
>
> I just want to do clean remove at OTG environment, like rmmod, so I did
> this when I worked on chipidea OTG design.
> I am worried if there are some resources dedicated for host device, eg,
> clocks, gpio. etc.
>
> If OTG framework can know well hcd's add and remove, it is ok for chipidea
> just calling usb_add_hcd/usb_del_hcd currently, but I suggested roger
> adding platform hcd_ops as optional parameter in case the platform
> has special requirement for hcd_ops.
>
We have 2 users now, omap + dwc3 and sh + EHCI/OHCI and we didn't seem to
observe any issues with the usb_add/del_hcd approach.

Let's keep things minimal for now. In the future, if users need something more
we can always extend the framework.

cheers,
-roger

2016-04-20 09:34:22

by Peter Chen

[permalink] [raw]
Subject: Re: [PATCH v6 07/12] usb: otg: add OTG/dual-role core

On Wed, Apr 20, 2016 at 09:54:49AM +0300, Roger Quadros wrote:
> On 18/04/16 05:09, Peter Chen wrote:
> > On Fri, Apr 15, 2016 at 02:00:46PM +0300, Roger Quadros wrote:
> >> On 15/04/16 12:25, Peter Chen wrote:
> >>> On Tue, Apr 05, 2016 at 05:05:12PM +0300, Roger Quadros wrote:
> >>>> + * usb_otg_register() - Register the OTG/dual-role device to OTG core
> >>>> + * @dev: OTG/dual-role controller device.
> >>>> + * @config: OTG configuration.
> >>>> + *
> >>>> + * Registers the OTG/dual-role controller device with the USB OTG core.
> >>>> + *
> >>>> + * Return: struct usb_otg * if success, ERR_PTR() if error.
> >>>> + */
> >>>> +struct usb_otg *usb_otg_register(struct device *dev,
> >>>> + struct usb_otg_config *config)
> >>>> +{
> >>>> + struct usb_otg *otg;
> >>>> + struct otg_wait_data *wait;
> >>>> + int ret = 0;
> >>>> +
> >>>> + if (!dev || !config || !config->fsm_ops)
> >>>> + return ERR_PTR(-EINVAL);
> >>>> +
> >>>> + /* already in list? */
> >>>> + mutex_lock(&otg_list_mutex);
> >>>> + if (usb_otg_get_data(dev)) {
> >>>> + dev_err(dev, "otg: %s: device already in otg list\n",
> >>>> + __func__);
> >>>> + ret = -EINVAL;
> >>>> + goto unlock;
> >>>> + }
> >>>> +
> >>>> + /* allocate and add to list */
> >>>> + otg = kzalloc(sizeof(*otg), GFP_KERNEL);
> >>>> + if (!otg) {
> >>>> + ret = -ENOMEM;
> >>>> + goto unlock;
> >>>> + }
> >>>> +
> >>>> + otg->dev = dev;
> >>>> + otg->caps = config->otg_caps;
> >>>> +
> >>>> + if ((otg->caps->hnp_support || otg->caps->srp_support ||
> >>>> + otg->caps->adp_support) && !config->otg_work)
> >>>> + dev_info(dev, "otg: limiting to dual-role\n");
> >>>
> >>> What does above mean? Customized otg_work item may be dual-role,
> >>> may be full otg.
> >>
> >> I'm checking for !config->otg_work so we're sure of using the
> >> default dual-role only work function.
> >>
> >
> > I see. But whether it is dual-role or fully otg should depend on
> > otg->caps, the work item may be different according to design.
> > Besides, your code seems to depend on one of the otg capabilities
> > for dual-role.
> >
> Which capability? Id/vbus status?
>

if (otg->caps->hnp_support || otg->caps->srp_support || otg->caps->adp_support)
it is fully otg;
else
it is dual role;

Do you think so?

--

Best Regards,
Peter Chen

2016-04-20 09:47:28

by Peter Chen

[permalink] [raw]
Subject: Re: [PATCH v6 07/12] usb: otg: add OTG/dual-role core

On Wed, Apr 20, 2016 at 10:02:33AM +0300, Roger Quadros wrote:
> On 19/04/16 11:06, Peter Chen wrote:
> > On Tue, Apr 05, 2016 at 05:05:12PM +0300, Roger Quadros wrote:
> >> +/**
> >> + * usb_otg_start_host - start/stop the host controller
> >> + * @otg: usb_otg instance
> >> + * @on: true to start, false to stop
> >> + *
> >> + * Start/stop the USB host controller. This function is meant
> >> + * for use by the OTG controller driver.
> >> + */
> >> +int usb_otg_start_host(struct usb_otg *otg, int on)
> >> +{
> >> + struct otg_hcd_ops *hcd_ops = otg->hcd_ops;
> >> +
> >> + dev_dbg(otg->dev, "otg: %s %d\n", __func__, on);
> >> + if (!otg->host) {
> >> + WARN_ONCE(1, "otg: fsm running without host\n");
> >> + return 0;
> >> + }
> >> +
> >> + if (on) {
> >> + if (otg->flags & OTG_FLAG_HOST_RUNNING)
> >> + return 0;
> >> +
> >> + otg->flags |= OTG_FLAG_HOST_RUNNING;
> >> +
> >> + /* start host */
> >> + hcd_ops->add(otg->primary_hcd.hcd, otg->primary_hcd.irqnum,
> >> + otg->primary_hcd.irqflags);
> >> + if (otg->shared_hcd.hcd) {
> >> + hcd_ops->add(otg->shared_hcd.hcd,
> >> + otg->shared_hcd.irqnum,
> >> + otg->shared_hcd.irqflags);
> >> + }
> >
> > Check the return value please.
>
> And what should we do on failure?
> Even if things fail, they could potentially start working on next
> remove/add iteration so I didn't bother checking return values.
>

If usb_add_hcd has failed, the hcd may be released (usb_put_hcd is
called), in that case, we can't call usb_remove_hcd, maybe we may
need to add hcd valid check for primary hcd too. Even we can't
stop fsm, we need to show an error message for user.

Chipidea idea have a bug before:

commit 41314fea2ffb6dc716b7e698a47c68b329602fc0
Author: Russell King - ARM Linux <[email protected]>
Date: Wed Oct 16 13:45:15 2013 +0100

usb/chipidea: fix oops on memory allocation failure

--

Best Regards,
Peter Chen

2016-04-20 09:48:28

by Peter Chen

[permalink] [raw]
Subject: Re: [PATCH v6 01/12] usb: hcd: Initialize hcd->flags to 0

On Wed, Apr 20, 2016 at 11:15:30AM +0300, Roger Quadros wrote:
> On 19/04/16 04:56, Peter Chen wrote:
> > On Mon, Apr 18, 2016 at 10:11:29AM -0400, Alan Stern wrote:
> >> On Mon, 18 Apr 2016, Peter Chen wrote:
> >>
> >>> On Wed, Apr 06, 2016 at 09:32:22AM +0300, Roger Quadros wrote:
> >>>> On 06/04/16 09:09, Felipe Balbi wrote:
> >>>>>
> >>>>> Hi,
> >>>>>
> >>>>> Roger Quadros <[email protected]> writes:
> >>>>>> diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
> >>>>>> index 2ca2cef..6b1930d 100644
> >>>>>> --- a/drivers/usb/core/hcd.c
> >>>>>> +++ b/drivers/usb/core/hcd.c
> >>>>>> @@ -2706,6 +2706,7 @@ int usb_add_hcd(struct usb_hcd *hcd,
> >>>>>> int retval;
> >>>>>> struct usb_device *rhdev;
> >>>>>>
> >>>>>> + hcd->flags = 0;
> >>>>>
> >>>>> seems like this would make more sense in usb_del_hcd() instead.
> >>>>>
> >>>>
> >>>> OK, I'll move it there.
> >>>>
> >>>
> >>> It depends on Alan's comments, whether only usb_add_hcd/usb_del_hcd
> >>> pair can be called repeat. If Alan acks it, I have no idea for it.
> >>
> >> Most of the host controller drivers were not written with this in mind,
> >> but I think it would be a good thing to allow. It would speed up the
> >> host/device role switches.
> >>
> >> This might mean we need to fix up several drivers to make them work
> >> correctly in an OTG environment. It should be possible to do this. Is
> >> there any particular reason why it would be difficult for Chipidea?
> >>
> >
> > I just want to do clean remove at OTG environment, like rmmod, so I did
> > this when I worked on chipidea OTG design.
> > I am worried if there are some resources dedicated for host device, eg,
> > clocks, gpio. etc.
> >
> > If OTG framework can know well hcd's add and remove, it is ok for chipidea
> > just calling usb_add_hcd/usb_del_hcd currently, but I suggested roger
> > adding platform hcd_ops as optional parameter in case the platform
> > has special requirement for hcd_ops.
> >
> We have 2 users now, omap + dwc3 and sh + EHCI/OHCI and we didn't seem to
> observe any issues with the usb_add/del_hcd approach.
>
> Let's keep things minimal for now. In the future, if users need something more
> we can always extend the framework.
>

I agree

--

Best Regards,
Peter Chen

2016-04-21 06:38:22

by Jun Li

[permalink] [raw]
Subject: RE: [PATCH v6 09/12] usb: gadget: udc: adapt to OTG core

Hi,

...
>
> /**
> + * usb_gadget_start - start the usb gadget controller and connect to
> +bus
> + * @gadget: the gadget device to start
> + *
> + * This is external API for use by OTG core.
> + *
> + * Start the usb device controller and connect to bus (enable pull).
> + */
> +static int usb_gadget_start(struct usb_gadget *gadget) {
> + int ret;
> + struct usb_udc *udc = NULL;
> +
> + dev_dbg(&gadget->dev, "%s\n", __func__);
> + mutex_lock(&udc_lock);
> + list_for_each_entry(udc, &udc_list, list)
> + if (udc->gadget == gadget)
> + goto found;
> +
> + dev_err(gadget->dev.parent, "%s: gadget not registered.\n",
> + __func__);
> + mutex_unlock(&udc_lock);
> + return -EINVAL;
> +
> +found:
> + ret = usb_gadget_udc_start(udc);
> + if (ret)
> + dev_err(&udc->dev, "USB Device Controller didn't start: %d\n",
> + ret);
> + else
> + usb_udc_connect_control(udc);

For drd, it's fine, but for real otg, gadget connect should be done
by loc_conn() instead of gadget start.

> +
> + mutex_unlock(&udc_lock);
> +
> + return ret;
> +}
> +
> +/**
> + * usb_gadget_stop - disconnect from bus and stop the usb gadget
> + * @gadget: The gadget device we want to stop
> + *
> + * This is external API for use by OTG core.
> + *
> + * Disconnect from the bus (disable pull) and stop the
> + * gadget controller.
> + */
> +static int usb_gadget_stop(struct usb_gadget *gadget) {
> + struct usb_udc *udc = NULL;
> +
> + dev_dbg(&gadget->dev, "%s\n", __func__);
> + mutex_lock(&udc_lock);
> + list_for_each_entry(udc, &udc_list, list)
> + if (udc->gadget == gadget)
> + goto found;
> +
> + dev_err(gadget->dev.parent, "%s: gadget not registered.\n",
> + __func__);
> + mutex_unlock(&udc_lock);
> + return -EINVAL;
> +
> +found:
> + usb_gadget_disconnect(udc->gadget);

Likewise, gadget disconnect also should be done by loc_conn()
instead of gadget stop.

> + udc->driver->disconnect(udc->gadget);
> + usb_gadget_udc_stop(udc);
> + mutex_unlock(&udc_lock);
> +
> + return 0;
> +}
> +

Li Jun

2016-04-21 07:00:18

by Peter Chen

[permalink] [raw]
Subject: Re: [PATCH v6 07/12] usb: otg: add OTG/dual-role core

On Tue, Apr 05, 2016 at 05:05:12PM +0300, Roger Quadros wrote:
> It provides APIs for the following tasks
>
> - Registering an OTG/dual-role capable controller
> - Registering Host and Gadget controllers to OTG core
> - Providing inputs to and kicking the OTG state machine
>
> Provide a dual-role device (DRD) state machine.
> DRD mode is a reduced functionality OTG mode. In this mode
> we don't support SRP, HNP and dynamic role-swap.
>
> In DRD operation, the controller mode (Host or Peripheral)
> is decided based on the ID pin status. Once a cable plug (Type-A
> or Type-B) is attached the controller selects the state
> and doesn't change till the cable in unplugged and a different
> cable type is inserted.
>
> As we don't need most of the complex OTG states and OTG timers
> we implement a lean DRD state machine in usb-otg.c.
> The DRD state machine is only interested in 2 hardware inputs
> 'id' and 'b_sess_vld'.
>
> Signed-off-by: Roger Quadros <[email protected]>
> ---
> drivers/usb/common/Makefile | 2 +-
> drivers/usb/common/usb-otg.c | 1058 ++++++++++++++++++++++++++++++++++++++++++
> drivers/usb/common/usb-otg.h | 71 +++
> drivers/usb/core/Kconfig | 2 +-
> include/linux/usb/gadget.h | 2 +
> include/linux/usb/hcd.h | 1 +
> include/linux/usb/otg-fsm.h | 7 +
> include/linux/usb/otg.h | 154 +++++-
> 8 files changed, 1292 insertions(+), 5 deletions(-)
> create mode 100644 drivers/usb/common/usb-otg.c
> create mode 100644 drivers/usb/common/usb-otg.h
>
> diff --git a/drivers/usb/common/Makefile b/drivers/usb/common/Makefile
> index f8f2c88..730d928 100644
> --- a/drivers/usb/common/Makefile
> +++ b/drivers/usb/common/Makefile
> @@ -7,5 +7,5 @@ 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
> +usbotg-y := usb-otg.o usb-otg-fsm.o
> obj-$(CONFIG_USB_OTG) += usbotg.o
> diff --git a/drivers/usb/common/usb-otg.c b/drivers/usb/common/usb-otg.c
> new file mode 100644
> index 0000000..41e762a
> --- /dev/null
> +++ b/drivers/usb/common/usb-otg.c
> @@ -0,0 +1,1058 @@
> +/**
> + * drivers/usb/common/usb-otg.c - USB OTG core
> + *
> + * Copyright (C) 2016 Texas Instruments Incorporated - http://www.ti.com
> + * Author: Roger Quadros <[email protected]>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/ktime.h>
> +#include <linux/hrtimer.h>
> +#include <linux/list.h>
> +#include <linux/of.h>
> +#include <linux/of_platform.h>
> +#include <linux/usb/otg.h>
> +#include <linux/usb/gadget.h>
> +#include <linux/workqueue.h>
> +
> +#include "usb-otg.h"
> +
> +struct otg_gcd {
> + struct usb_gadget *gadget;
> + struct otg_gadget_ops *ops;
> +};
> +
> +/* OTG device list */
> +LIST_HEAD(otg_list);
> +static DEFINE_MUTEX(otg_list_mutex);
> +
> +/* Hosts and Gadgets waiting for OTG controller */
> +struct otg_wait_data {
> + struct device *dev; /* OTG controller device */
> +
> + struct otg_hcd primary_hcd;
> + struct otg_hcd shared_hcd;
> + struct otg_gcd gcd;
> + struct list_head list;
> +};
> +
> +LIST_HEAD(wait_list);
> +static DEFINE_MUTEX(wait_list_mutex);
> +
> +static int usb_otg_hcd_is_primary_hcd(struct usb_hcd *hcd)
> +{
> + if (!hcd->primary_hcd)
> + return 1;
> + return hcd == hcd->primary_hcd;
> +}

Just find there is already a usb_hcd_is_primary_hcd at hcd.c,
would you use it directly?

Peter
> +
> +/**
> + * Check if the OTG device is in our wait list and return
> + * otg_wait_data, else NULL.
> + *
> + * wait_list_mutex must be held.
> + */
> +static struct otg_wait_data *usb_otg_get_wait(struct device *otg_dev)
> +{
> + struct otg_wait_data *wait;
> +
> + if (!otg_dev)
> + return NULL;
> +
> + /* is there an entry for this otg_dev ?*/
> + list_for_each_entry(wait, &wait_list, list) {
> + if (wait->dev == otg_dev)
> + return wait;
> + }
> +
> + return NULL;
> +}
> +
> +/**
> + * Add the hcd to our wait list
> + */
> +static int usb_otg_hcd_wait_add(struct device *otg_dev, struct usb_hcd *hcd,
> + unsigned int irqnum, unsigned long irqflags,
> + struct otg_hcd_ops *ops)
> +{
> + struct otg_wait_data *wait;
> + int ret = -EINVAL;
> +
> + mutex_lock(&wait_list_mutex);
> +
> + wait = usb_otg_get_wait(otg_dev);
> + if (!wait) {
> + /* Not yet in wait list? allocate and add */
> + wait = kzalloc(sizeof(*wait), GFP_KERNEL);
> + if (!wait) {
> + ret = -ENOMEM;
> + goto fail;
> + }
> +
> + wait->dev = otg_dev;
> + list_add_tail(&wait->list, &wait_list);
> + }
> +
> + if (usb_otg_hcd_is_primary_hcd(hcd)) {
> + if (wait->primary_hcd.hcd) /* already assigned? */
> + goto fail;
> +
> + wait->primary_hcd.hcd = hcd;
> + wait->primary_hcd.irqnum = irqnum;
> + wait->primary_hcd.irqflags = irqflags;
> + wait->primary_hcd.ops = ops;
> + wait->primary_hcd.otg_dev = otg_dev;
> + } else {
> + if (wait->shared_hcd.hcd) /* already assigned? */
> + goto fail;
> +
> + wait->shared_hcd.hcd = hcd;
> + wait->shared_hcd.irqnum = irqnum;
> + wait->shared_hcd.irqflags = irqflags;
> + wait->shared_hcd.ops = ops;
> + wait->shared_hcd.otg_dev = otg_dev;
> + }
> +
> + mutex_unlock(&wait_list_mutex);
> + return 0;
> +
> +fail:
> + mutex_unlock(&wait_list_mutex);
> + return ret;
> +}
> +
> +/**
> + * Check and free wait list entry if empty
> + *
> + * wait_list_mutex must be held
> + */
> +static void usb_otg_check_free_wait(struct otg_wait_data *wait)
> +{
> + if (wait->primary_hcd.hcd || wait->shared_hcd.hcd || wait->gcd.gadget)
> + return;
> +
> + list_del(&wait->list);
> + kfree(wait);
> +}
> +
> +/**
> + * Remove the hcd from our wait list
> + */
> +static int usb_otg_hcd_wait_remove(struct usb_hcd *hcd)
> +{
> + struct otg_wait_data *wait;
> +
> + mutex_lock(&wait_list_mutex);
> +
> + /* is there an entry for this hcd ?*/
> + list_for_each_entry(wait, &wait_list, list) {
> + if (wait->primary_hcd.hcd == hcd) {
> + wait->primary_hcd.hcd = 0;
> + goto found;
> + } else if (wait->shared_hcd.hcd == hcd) {
> + wait->shared_hcd.hcd = 0;
> + goto found;
> + }
> + }
> +
> + mutex_unlock(&wait_list_mutex);
> + return -EINVAL;
> +
> +found:
> + usb_otg_check_free_wait(wait);
> + mutex_unlock(&wait_list_mutex);
> +
> + return 0;
> +}
> +
> +/**
> + * Add the gadget to our wait list
> + */
> +static int usb_otg_gadget_wait_add(struct device *otg_dev,
> + struct usb_gadget *gadget,
> + struct otg_gadget_ops *ops)
> +{
> + struct otg_wait_data *wait;
> + int ret = -EINVAL;
> +
> + mutex_lock(&wait_list_mutex);
> +
> + wait = usb_otg_get_wait(otg_dev);
> + if (!wait) {
> + /* Not yet in wait list? allocate and add */
> + wait = kzalloc(sizeof(*wait), GFP_KERNEL);
> + if (!wait) {
> + ret = -ENOMEM;
> + goto fail;
> + }
> +
> + wait->dev = otg_dev;
> + list_add_tail(&wait->list, &wait_list);
> + }
> +
> + if (wait->gcd.gadget) /* already assigned? */
> + goto fail;
> +
> + wait->gcd.gadget = gadget;
> + wait->gcd.ops = ops;
> + mutex_unlock(&wait_list_mutex);
> +
> + return 0;
> +
> +fail:
> + mutex_unlock(&wait_list_mutex);
> + return ret;
> +}
> +
> +/**
> + * Remove the gadget from our wait list
> + */
> +static int usb_otg_gadget_wait_remove(struct usb_gadget *gadget)
> +{
> + struct otg_wait_data *wait;
> +
> + mutex_lock(&wait_list_mutex);
> +
> + /* is there an entry for this gadget ?*/
> + list_for_each_entry(wait, &wait_list, list) {
> + if (wait->gcd.gadget == gadget) {
> + wait->gcd.gadget = 0;
> + goto found;
> + }
> + }
> +
> + mutex_unlock(&wait_list_mutex);
> +
> + return -EINVAL;
> +
> +found:
> + usb_otg_check_free_wait(wait);
> + mutex_unlock(&wait_list_mutex);
> +
> + return 0;
> +}
> +
> +/**
> + * Register pending host/gadget and remove entry from wait list
> + */
> +static void usb_otg_flush_wait(struct device *otg_dev)
> +{
> + struct otg_wait_data *wait;
> + struct otg_hcd *host;
> + struct otg_gcd *gadget;
> +
> + mutex_lock(&wait_list_mutex);
> +
> + wait = usb_otg_get_wait(otg_dev);
> + if (!wait)
> + goto done;
> +
> + dev_dbg(otg_dev, "otg: registering pending host/gadget\n");
> + gadget = &wait->gcd;
> + if (gadget)
> + usb_otg_register_gadget(gadget->gadget, gadget->ops);
> +
> + host = &wait->primary_hcd;
> + if (host->hcd)
> + usb_otg_register_hcd(host->hcd, host->irqnum, host->irqflags,
> + host->ops);
> +
> + host = &wait->shared_hcd;
> + if (host->hcd)
> + usb_otg_register_hcd(host->hcd, host->irqnum, host->irqflags,
> + host->ops);
> +
> + list_del(&wait->list);
> + kfree(wait);
> +
> +done:
> + mutex_unlock(&wait_list_mutex);
> +}
> +
> +/**
> + * Check if the OTG device is in our OTG list and return
> + * usb_otg data, else NULL.
> + *
> + * otg_list_mutex must be held.
> + */
> +static struct usb_otg *usb_otg_get_data(struct device *otg_dev)
> +{
> + struct usb_otg *otg;
> +
> + if (!otg_dev)
> + return NULL;
> +
> + list_for_each_entry(otg, &otg_list, list) {
> + if (otg->dev == otg_dev)
> + return otg;
> + }
> +
> + return NULL;
> +}
> +
> +/**
> + * usb_otg_start_host - start/stop the host controller
> + * @otg: usb_otg instance
> + * @on: true to start, false to stop
> + *
> + * Start/stop the USB host controller. This function is meant
> + * for use by the OTG controller driver.
> + */
> +int usb_otg_start_host(struct usb_otg *otg, int on)
> +{
> + struct otg_hcd_ops *hcd_ops = otg->hcd_ops;
> +
> + dev_dbg(otg->dev, "otg: %s %d\n", __func__, on);
> + if (!otg->host) {
> + WARN_ONCE(1, "otg: fsm running without host\n");
> + return 0;
> + }
> +
> + if (on) {
> + if (otg->flags & OTG_FLAG_HOST_RUNNING)
> + return 0;
> +
> + otg->flags |= OTG_FLAG_HOST_RUNNING;
> +
> + /* start host */
> + hcd_ops->add(otg->primary_hcd.hcd, otg->primary_hcd.irqnum,
> + otg->primary_hcd.irqflags);
> + if (otg->shared_hcd.hcd) {
> + hcd_ops->add(otg->shared_hcd.hcd,
> + otg->shared_hcd.irqnum,
> + otg->shared_hcd.irqflags);
> + }
> + } else {
> + if (!(otg->flags & OTG_FLAG_HOST_RUNNING))
> + return 0;
> +
> + otg->flags &= ~OTG_FLAG_HOST_RUNNING;
> +
> + /* stop host */
> + if (otg->shared_hcd.hcd)
> + hcd_ops->remove(otg->shared_hcd.hcd);
> +
> + hcd_ops->remove(otg->primary_hcd.hcd);
> + }
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(usb_otg_start_host);
> +
> +/**
> + * usb_otg_start_gadget - start/stop the gadget controller
> + * @otg: usb_otg instance
> + * @on: true to start, false to stop
> + *
> + * Start/stop the USB gadget controller. This function is meant
> + * for use by the OTG controller driver.
> + */
> +int usb_otg_start_gadget(struct usb_otg *otg, int on)
> +{
> + struct usb_gadget *gadget = otg->gadget;
> +
> + dev_dbg(otg->dev, "otg: %s %d\n", __func__, on);
> + if (!gadget) {
> + WARN_ONCE(1, "otg: fsm running without gadget\n");
> + return 0;
> + }
> +
> + if (on) {
> + if (otg->flags & OTG_FLAG_GADGET_RUNNING)
> + return 0;
> +
> + otg->flags |= OTG_FLAG_GADGET_RUNNING;
> + otg->gadget_ops->start(otg->gadget);
> + } else {
> + if (!(otg->flags & OTG_FLAG_GADGET_RUNNING))
> + return 0;
> +
> + otg->flags &= ~OTG_FLAG_GADGET_RUNNING;
> + otg->gadget_ops->stop(otg->gadget);
> + }
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(usb_otg_start_gadget);
> +
> +/**
> + * Change USB protocol when there is a protocol change.
> + * fsm->lock must be held.
> + */
> +static int drd_set_protocol(struct otg_fsm *fsm, int protocol)
> +{
> + struct usb_otg *otg = container_of(fsm, struct usb_otg, fsm);
> + int ret = 0;
> +
> + if (fsm->protocol != protocol) {
> + dev_dbg(otg->dev, "otg: changing role fsm->protocol= %d; new protocol= %d\n",
> + fsm->protocol, protocol);
> + /* stop old protocol */
> + if (fsm->protocol == PROTO_HOST)
> + ret = otg_start_host(otg, 0);
> + else if (fsm->protocol == PROTO_GADGET)
> + 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);
> + if (ret)
> + return ret;
> +
> + fsm->protocol = protocol;
> + return 0;
> + }
> +
> + return 0;
> +}
> +
> +/**
> + * Called when entering a DRD state.
> + * fsm->lock must be held.
> + */
> +static void drd_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state)
> +{
> + struct usb_otg *otg = container_of(fsm, struct usb_otg, fsm);
> +
> + if (otg->state == new_state)
> + return;
> +
> + fsm->state_changed = 1;
> + dev_dbg(otg->dev, "otg: set state: %s\n",
> + usb_otg_state_string(new_state));
> + switch (new_state) {
> + case OTG_STATE_B_IDLE:
> + drd_set_protocol(fsm, PROTO_UNDEF);
> + otg_drv_vbus(otg, 0);
> + break;
> + case OTG_STATE_B_PERIPHERAL:
> + drd_set_protocol(fsm, PROTO_GADGET);
> + otg_drv_vbus(otg, 0);
> + break;
> + case OTG_STATE_A_HOST:
> + drd_set_protocol(fsm, PROTO_HOST);
> + otg_drv_vbus(otg, 1);
> + break;
> + 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(otg->dev, "%s: otg: invalid state: %s\n",
> + __func__, usb_otg_state_string(new_state));
> + break;
> + }
> +
> + otg->state = new_state;
> +}
> +
> +/**
> + * DRD state change judgement
> + *
> + * For DRD we're only interested in some of the OTG states
> + * i.e. OTG_STATE_B_IDLE: both peripheral and host are stopped
> + * OTG_STATE_B_PERIPHERAL: peripheral active
> + * OTG_STATE_A_HOST: host active
> + * we're only interested in the following inputs
> + * fsm->id, fsm->b_sess_vld
> + */
> +int drd_statemachine(struct usb_otg *otg)
> +{
> + struct otg_fsm *fsm = &otg->fsm;
> + enum usb_otg_state state;
> + int ret;
> +
> + mutex_lock(&fsm->lock);
> +
> + fsm->state_changed = 0;
> + state = otg->state;
> +
> + switch (state) {
> + case OTG_STATE_UNDEFINED:
> + if (!fsm->id)
> + drd_set_state(fsm, OTG_STATE_A_HOST);
> + else if (fsm->id && fsm->b_sess_vld)
> + drd_set_state(fsm, OTG_STATE_B_PERIPHERAL);
> + else
> + drd_set_state(fsm, OTG_STATE_B_IDLE);
> + break;
> + case OTG_STATE_B_IDLE:
> + if (!fsm->id)
> + drd_set_state(fsm, OTG_STATE_A_HOST);
> + else if (fsm->b_sess_vld)
> + drd_set_state(fsm, OTG_STATE_B_PERIPHERAL);
> + break;
> + case OTG_STATE_B_PERIPHERAL:
> + if (!fsm->id)
> + drd_set_state(fsm, OTG_STATE_A_HOST);
> + else if (!fsm->b_sess_vld)
> + drd_set_state(fsm, OTG_STATE_B_IDLE);
> + break;
> + case OTG_STATE_A_HOST:
> + if (fsm->id && fsm->b_sess_vld)
> + drd_set_state(fsm, OTG_STATE_B_PERIPHERAL);
> + else if (fsm->id && !fsm->b_sess_vld)
> + drd_set_state(fsm, OTG_STATE_B_IDLE);
> + break;
> +
> + /* 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(otg->dev, "%s: otg: invalid usb-drd state: %s\n",
> + __func__, usb_otg_state_string(state));
> + drd_set_state(fsm, OTG_STATE_UNDEFINED);
> + 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);
> +
> +/**
> + * OTG FSM/DRD work function
> + */
> +static void usb_otg_work(struct work_struct *work)
> +{
> + struct usb_otg *otg = container_of(work, struct usb_otg, work);
> +
> + pm_runtime_get_sync(otg->dev);
> + drd_statemachine(otg);
> + pm_runtime_put_sync(otg->dev);
> +}
> +
> +/**
> + * usb_otg_register() - Register the OTG/dual-role device to OTG core
> + * @dev: OTG/dual-role controller device.
> + * @config: OTG configuration.
> + *
> + * Registers the OTG/dual-role controller device with the USB OTG core.
> + *
> + * Return: struct usb_otg * if success, ERR_PTR() if error.
> + */
> +struct usb_otg *usb_otg_register(struct device *dev,
> + struct usb_otg_config *config)
> +{
> + struct usb_otg *otg;
> + struct otg_wait_data *wait;
> + int ret = 0;
> +
> + if (!dev || !config || !config->fsm_ops)
> + return ERR_PTR(-EINVAL);
> +
> + /* already in list? */
> + mutex_lock(&otg_list_mutex);
> + if (usb_otg_get_data(dev)) {
> + dev_err(dev, "otg: %s: device already in otg list\n",
> + __func__);
> + ret = -EINVAL;
> + goto unlock;
> + }
> +
> + /* allocate and add to list */
> + otg = kzalloc(sizeof(*otg), GFP_KERNEL);
> + if (!otg) {
> + ret = -ENOMEM;
> + goto unlock;
> + }
> +
> + otg->dev = dev;
> + otg->caps = config->otg_caps;
> +
> + if ((otg->caps->hnp_support || otg->caps->srp_support ||
> + otg->caps->adp_support) && !config->otg_work)
> + dev_info(dev, "otg: limiting to dual-role\n");
> +
> + if (config->otg_work) /* custom otg_work ? */
> + INIT_WORK(&otg->work, config->otg_work);
> + else
> + INIT_WORK(&otg->work, usb_otg_work);
> +
> + otg->wq = create_singlethread_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);
> +
> + /* were we in wait list? */
> + mutex_lock(&wait_list_mutex);
> + wait = usb_otg_get_wait(dev);
> + mutex_unlock(&wait_list_mutex);
> + if (wait) {
> + /* register pending host/gadget and flush from list */
> + usb_otg_flush_wait(dev);
> + }
> +
> + return otg;
> +
> +err_wq:
> + kfree(otg);
> +unlock:
> + mutex_unlock(&otg_list_mutex);
> + return ERR_PTR(ret);
> +}
> +EXPORT_SYMBOL_GPL(usb_otg_register);
> +
> +/**
> + * usb_otg_unregister() - Unregister the OTG/dual-role device from USB OTG core
> + * @dev: OTG controller device.
> + *
> + * Unregisters the OTG/dual-role controller device from USB OTG core.
> + * Prevents unregistering till both the associated Host and Gadget controllers
> + * have unregistered from the OTG core.
> + *
> + * Return: 0 on success, error value otherwise.
> + */
> +int usb_otg_unregister(struct device *dev)
> +{
> + struct usb_otg *otg;
> +
> + mutex_lock(&otg_list_mutex);
> + otg = usb_otg_get_data(dev);
> + if (!otg) {
> + dev_err(dev, "otg: %s: device not in otg list\n",
> + __func__);
> + mutex_unlock(&otg_list_mutex);
> + return -EINVAL;
> + }
> +
> + /* prevent unregister till both host & gadget have unregistered */
> + if (otg->host || otg->gadget) {
> + dev_err(dev, "otg: %s: host/gadget still registered\n",
> + __func__);
> + return -EBUSY;
> + }
> +
> + /* OTG FSM is halted when host/gadget unregistered */
> + destroy_workqueue(otg->wq);
> +
> + /* remove from otg list */
> + list_del(&otg->list);
> + kfree(otg);
> + mutex_unlock(&otg_list_mutex);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(usb_otg_unregister);
> +
> +/**
> + * start/kick the OTG FSM if we can
> + * fsm->lock must be held
> + */
> +static void usb_otg_start_fsm(struct usb_otg *otg)
> +{
> + struct otg_fsm *fsm = &otg->fsm;
> +
> + if (fsm->running)
> + goto kick_fsm;
> +
> + if (!otg->host) {
> + dev_info(otg->dev, "otg: can't start till host registers\n");
> + return;
> + }
> +
> + if (!otg->gadget) {
> + dev_info(otg->dev, "otg: can't start till gadget registers\n");
> + return;
> + }
> +
> + fsm->running = true;
> +kick_fsm:
> + queue_work(otg->wq, &otg->work);
> +}
> +
> +/**
> + * stop the OTG FSM. Stops Host & Gadget controllers as well.
> + * fsm->lock must be held
> + */
> +static void usb_otg_stop_fsm(struct usb_otg *otg)
> +{
> + struct otg_fsm *fsm = &otg->fsm;
> +
> + if (!fsm->running)
> + return;
> +
> + /* no more new events queued */
> + fsm->running = false;
> +
> + flush_workqueue(otg->wq);
> + otg->state = OTG_STATE_UNDEFINED;
> +
> + /* stop host/gadget immediately */
> + if (fsm->protocol == PROTO_HOST)
> + otg_start_host(otg, 0);
> + else if (fsm->protocol == PROTO_GADGET)
> + otg_start_gadget(otg, 0);
> + fsm->protocol = PROTO_UNDEF;
> +}
> +
> +/**
> + * usb_otg_sync_inputs - Sync OTG inputs with the OTG state machine
> + * @fsm: OTG FSM instance
> + *
> + * Used by the OTG driver to update the inputs to the OTG
> + * state machine.
> + *
> + * Can be called in IRQ context.
> + */
> +void usb_otg_sync_inputs(struct usb_otg *otg)
> +{
> + /* Don't kick FSM till it has started */
> + if (!otg->fsm.running)
> + return;
> +
> + /* Kick FSM */
> + queue_work(otg->wq, &otg->work);
> +}
> +EXPORT_SYMBOL_GPL(usb_otg_sync_inputs);
> +
> +/**
> + * usb_otg_kick_fsm - Kick the OTG state machine
> + * @otg_dev: OTG controller device
> + *
> + * Used by USB host/device stack to sync OTG related
> + * events to the OTG state machine.
> + * e.g. change in host_bus->b_hnp_enable, gadget->b_hnp_enable
> + *
> + * Returns: 0 on success, error value otherwise.
> + */
> +int usb_otg_kick_fsm(struct device *otg_dev)
> +{
> + struct usb_otg *otg;
> +
> + mutex_lock(&otg_list_mutex);
> + otg = usb_otg_get_data(otg_dev);
> + mutex_unlock(&otg_list_mutex);
> + if (!otg) {
> + dev_dbg(otg_dev, "otg: %s: invalid otg device\n",
> + __func__);
> + return -ENODEV;
> + }
> +
> + usb_otg_sync_inputs(otg);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(usb_otg_kick_fsm);
> +
> +/**
> + * usb_otg_register_hcd - Register the host controller to OTG core
> + * @hcd: host controller device
> + * @irqnum: interrupt number
> + * @irqflags: interrupt flags
> + * @ops: HCD ops to interface with the HCD
> + *
> + * This is used by the USB Host stack to register the host controller
> + * to the OTG core. Host controller must not be started by the
> + * caller as it is left upto the OTG state machine to do so.
> + * hcd->otg_dev must contain the related otg controller device.
> + *
> + * Returns: 0 on success, error value otherwise.
> + */
> +int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
> + unsigned long irqflags, struct otg_hcd_ops *ops)
> +{
> + struct usb_otg *otg;
> + struct device *hcd_dev = hcd->self.controller;
> + struct device *otg_dev = hcd->otg_dev;
> +
> + if (!otg_dev)
> + return -EINVAL;
> +
> + /* we're otg but otg controller might not yet be registered */
> + mutex_lock(&otg_list_mutex);
> + otg = usb_otg_get_data(otg_dev);
> + mutex_unlock(&otg_list_mutex);
> + if (!otg) {
> + dev_dbg(hcd_dev,
> + "otg: controller not yet registered. waiting..\n");
> + /*
> + * otg controller might register later. Put the hcd in
> + * wait list and call us back when ready
> + */
> + if (usb_otg_hcd_wait_add(otg_dev, hcd, irqnum, irqflags, ops)) {
> + dev_err(hcd_dev, "otg: failed to add hcd to wait list\n");
> + return -EINVAL;
> + }
> +
> + return 0;
> + }
> +
> + /* 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) {
> + /* are we in wait list? */
> + if (!usb_otg_hcd_wait_remove(hcd))
> + return 0;
> +
> + dev_dbg(hcd_dev, "otg: host wasn't registered with otg\n");
> + return -EINVAL;
> + }
> +
> + mutex_lock(&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 {
> + dev_err(otg_dev, "otg: host %s wasn't registered with otg\n",
> + dev_name(hcd_dev));
> + mutex_unlock(&otg->fsm.lock);
> + return -EINVAL;
> + }
> +
> + /* stop FSM & Host */
> + usb_otg_stop_fsm(otg);
> + otg->host = NULL;
> +
> + mutex_unlock(&otg->fsm.lock);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(usb_otg_unregister_hcd);
> +
> +/**
> + * usb_otg_register_gadget - Register the gadget controller to OTG core
> + * @gadget: gadget controller
> + *
> + * This is used by the USB gadget stack to register the gadget controller
> + * to the OTG core. Gadget controller must not be started by the
> + * caller as it is left upto the OTG state machine to do so.
> + *
> + * Gadget core must call this only when all resources required for
> + * gadget controller to run are available.
> + * i.e. gadget function driver is available.
> + *
> + * Returns: 0 on success, error value otherwise.
> + */
> +int usb_otg_register_gadget(struct usb_gadget *gadget,
> + struct otg_gadget_ops *ops)
> +{
> + struct usb_otg *otg;
> + struct device *gadget_dev = &gadget->dev;
> + struct device *otg_dev = gadget->otg_dev;
> +
> + if (!otg_dev)
> + return -EINVAL; /* we're definitely not OTG */
> +
> + /* we're otg but otg controller might not yet be registered */
> + mutex_lock(&otg_list_mutex);
> + otg = usb_otg_get_data(otg_dev);
> + mutex_unlock(&otg_list_mutex);
> + if (!otg) {
> + dev_dbg(gadget_dev,
> + "otg: controller not yet registered. waiting..\n");
> + /*
> + * otg controller might register later. Put the gadget in
> + * wait list and call us back when ready
> + */
> + if (usb_otg_gadget_wait_add(otg_dev, gadget, ops)) {
> + dev_err(gadget_dev,
> + "otg: failed to add to gadget to wait list\n");
> + return -EINVAL;
> + }
> +
> + return 0;
> + }
> +
> + 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));
> +
> + /* start FSM */
> + usb_otg_start_fsm(otg);
> + mutex_unlock(&otg->fsm.lock);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(usb_otg_register_gadget);
> +
> +/**
> + * usb_otg_unregister_gadget - Unregister the gadget controller from OTG core
> + * @gadget: gadget controller
> + *
> + * This is used by the USB gadget stack to unregister the gadget controller
> + * from the OTG core. Ensures that gadget controller is halted
> + * on successful return.
> + *
> + * Returns: 0 on success, error value otherwise.
> + */
> +int usb_otg_unregister_gadget(struct usb_gadget *gadget)
> +{
> + struct usb_otg *otg;
> + struct device *gadget_dev = &gadget->dev;
> + struct device *otg_dev = gadget->otg_dev;
> +
> + if (!otg_dev)
> + return -EINVAL;
> +
> + mutex_lock(&otg_list_mutex);
> + otg = usb_otg_get_data(otg_dev);
> + mutex_unlock(&otg_list_mutex);
> + if (!otg) {
> + /* are we in wait list? */
> + if (!usb_otg_gadget_wait_remove(gadget))
> + return 0;
> +
> + dev_dbg(gadget_dev, "otg: gadget wasn't registered with otg\n");
> + return -EINVAL;
> + }
> +
> + mutex_lock(&otg->fsm.lock);
> + if (otg->gadget != gadget) {
> + dev_err(otg_dev, "otg: gadget %s wasn't registered with otg\n",
> + dev_name(&gadget->dev));
> + mutex_unlock(&otg->fsm.lock);
> + return -EINVAL;
> + }
> +
> + /* Stop FSM & gadget */
> + 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);
> diff --git a/drivers/usb/common/usb-otg.h b/drivers/usb/common/usb-otg.h
> new file mode 100644
> index 0000000..2bf3fbf
> --- /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) 2016 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 ae228d0..b468a9f 100644
> --- a/drivers/usb/core/Kconfig
> +++ b/drivers/usb/core/Kconfig
> @@ -42,7 +42,7 @@ config USB_DYNAMIC_MINORS
> If you are unsure about this, say N here.
>
> config USB_OTG
> - bool "OTG support"
> + bool "OTG/Dual-role support"
> depends on PM
> default n
> help
> diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h
> index 8c0ae64..1878ae1 100644
> --- a/include/linux/usb/gadget.h
> +++ b/include/linux/usb/gadget.h
> @@ -583,6 +583,7 @@ struct usb_gadget_ops {
> * @out_epnum: last used out ep number
> * @in_epnum: last used in ep number
> * @otg_caps: OTG capabilities of this gadget.
> + * @otg_dev: OTG controller device, if needs to be used with OTG core.
> * @sg_supported: true if we can handle scatter-gather
> * @is_otg: True if the USB device port uses a Mini-AB jack, so that the
> * gadget driver must provide a USB OTG descriptor.
> @@ -639,6 +640,7 @@ struct usb_gadget {
> unsigned out_epnum;
> unsigned in_epnum;
> struct usb_otg_caps *otg_caps;
> + struct device *otg_dev;
>
> unsigned sg_supported:1;
> unsigned is_otg:1;
> diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h
> index 861ccaa..2017cd4 100644
> --- a/include/linux/usb/hcd.h
> +++ b/include/linux/usb/hcd.h
> @@ -184,6 +184,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 36f0cf9..ba6755c 100644
> --- a/include/linux/usb/otg-fsm.h
> +++ b/include/linux/usb/otg-fsm.h
> @@ -61,6 +61,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
> @@ -133,6 +138,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 */
> @@ -188,6 +194,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..b094352 100644
> --- a/include/linux/usb/otg.h
> +++ b/include/linux/usb/otg.h
> @@ -10,10 +10,55 @@
> #define __LINUX_USB_OTG_H
>
> #include <linux/phy/phy.h>
> -#include <linux/usb/phy.h>
> -#include <linux/usb/otg-fsm.h>
> +#include <linux/device.h>
> +#include <linux/hrtimer.h>
> +#include <linux/ktime.h>
> +#include <linux/usb.h>
> #include <linux/usb/hcd.h>
> +#include <linux/usb/gadget.h>
> +#include <linux/usb/otg-fsm.h>
> +#include <linux/usb/phy.h>
>
> +/**
> + * struct otg_hcd - host controller state and interface
> + *
> + * @hcd: host controller
> + * @irqnum: irq number
> + * @irqflags: irq flags
> + * @ops: otg to host controller interface
> + * @ops: otg to host controller interface
> + * @otg_dev: otg controller device
> + */
> +struct otg_hcd {
> + struct usb_hcd *hcd;
> + unsigned int irqnum;
> + unsigned long irqflags;
> + struct otg_hcd_ops *ops;
> + struct device *otg_dev;
> +};
> +
> +/**
> + * struct usb_otg - 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 +69,24 @@ 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;
> + 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,7 +102,7 @@ struct usb_otg {
>
> /* start or continue HNP role switch */
> int (*start_hnp)(struct usb_otg *otg);
> -
> +/*---------------------------------------------------------------*/
> };
>
> /**
> @@ -60,8 +120,92 @@ struct usb_otg_caps {
> bool adp_support;
> };
>
> +/**
> + * struct usb_otg_config - otg controller configuration
> + * @caps: otg capabilities of the controller
> + * @ops: otg fsm operations
> + * @otg_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);
> +};
> +
> 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);
> +
> +#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;
> +}
> +#endif /* CONFIG_USB_OTG */
> +
> +/*------------- deprecated interface -----------------------------*/
> /* Context: can sleep */
> static inline int
> otg_start_hnp(struct usb_otg *otg)
> @@ -113,6 +257,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 +383,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.5.0
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-usb" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html

--

Best Regards,
Peter Chen

2016-04-22 01:34:23

by Peter Chen

[permalink] [raw]
Subject: Re: [PATCH v6 07/12] usb: otg: add OTG/dual-role core

On Fri, Apr 15, 2016 at 10:03:16AM +0000, Yoshihiro Shimoda wrote:
> Hi,
>
> > From: Yoshihiro Shimoda
> > Sent: Friday, April 15, 2016 6:59 PM
> >
> > Hi,
> >
> > > From: Roger Quadros
> > > Sent: Thursday, April 14, 2016 8:32 PM
> > >
> > > On 14/04/16 14:15, Yoshihiro Shimoda wrote:
> > > > Hi,
> > > >
> > < snip >
> > > >>> @@ -865,7 +867,8 @@ 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->caps->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 */
> > > >>>
> > > >>
> > > >> These changes look good to me. Thanks.
> > > >
> > > > Thank you for the comment.
> > > > If we change the "needs_companion" place to the otg_config,
> > > > do we need to add a flag into the otg, instead of otg->caps?
> > >
> > > Yes we can add a flag in struct usb_otg.
> >
> > Thank you for the comment.
> >
> > I made a fixed patch.
> > So, should I send this patch to ML after you sent v7 patches?
> > Or, would you apply this patch before you send v7 patches?
>
> Oops, I sent this email without my patch...
>
> ---
> Subject: [PATCH] usb: otg: add hcd companion support
>
> 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 <[email protected]>
> ---
> Documentation/devicetree/bindings/usb/generic.txt | 3 +++
> drivers/usb/common/usb-otg.c | 17 +++++++++++++----
> include/linux/usb/otg.h | 7 ++++++-
> 3 files changed, 22 insertions(+), 5 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 41e762a..83c8c96 100644
> --- a/drivers/usb/common/usb-otg.c
> +++ b/drivers/usb/common/usb-otg.c
> @@ -20,6 +20,7 @@
> #include <linux/list.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>
> @@ -600,6 +601,10 @@ struct usb_otg *usb_otg_register(struct device *dev,
> else
> INIT_WORK(&otg->work, usb_otg_work);
>
> + if (of_find_property(dev->of_node, "hcd-needs-companion", NULL) ||
> + config->hcd_needs_companion) /* needs comanion ? */
> + otg->flags |= OTG_FLAG_HCD_NEEDS_COMPANION;
> +
> otg->wq = create_singlethread_workqueue("usb_otg");
> if (!otg->wq) {
> dev_err(dev, "otg: %s: can't create workqueue\n",
> @@ -823,13 +828,15 @@ 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");
> goto err;
> @@ -865,7 +872,9 @@ 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 */
>
> diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h
> index b094352..6f4ca77 100644
> --- a/include/linux/usb/otg.h
> +++ b/include/linux/usb/otg.h
> @@ -57,7 +57,8 @@ struct otg_hcd {
> * @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;
> @@ -84,6 +85,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 -----------------------------*/
> @@ -125,11 +127,14 @@ struct usb_otg_caps {
> * @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);
> --

Hi Yoshihiro, the shared_hcd is used for USB3 only [1], this patch may
not be suitable for supporting companion controller at OTG framework.
For companion or other USB2 controllers, both primary hcd and shard hcd
are NULL for HCD core.

[1] http://www.spinics.net/lists/linux-usb/msg139630.html
--

Best Regards,
Peter Chen

2016-04-22 03:41:51

by Peter Chen

[permalink] [raw]
Subject: Re: [PATCH v6 07/12] usb: otg: add OTG/dual-role core

On Fri, Apr 22, 2016 at 09:26:46AM +0800, Peter Chen wrote:
> On Fri, Apr 15, 2016 at 10:03:16AM +0000, Yoshihiro Shimoda wrote:
> > Hi,
> >
> > > From: Yoshihiro Shimoda
> > > Sent: Friday, April 15, 2016 6:59 PM
> > >
> > > Hi,
> > >
> > > > From: Roger Quadros
> > > > Sent: Thursday, April 14, 2016 8:32 PM
> > > >
> > > > On 14/04/16 14:15, Yoshihiro Shimoda wrote:
> > > > > Hi,
> > > > >
> > > < snip >
> > > > >>> @@ -865,7 +867,8 @@ 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->caps->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 */
> > > > >>>
> > > > >>
> > > > >> These changes look good to me. Thanks.
> > > > >
> > > > > Thank you for the comment.
> > > > > If we change the "needs_companion" place to the otg_config,
> > > > > do we need to add a flag into the otg, instead of otg->caps?
> > > >
> > > > Yes we can add a flag in struct usb_otg.
> > >
> > > Thank you for the comment.
> > >
> > > I made a fixed patch.
> > > So, should I send this patch to ML after you sent v7 patches?
> > > Or, would you apply this patch before you send v7 patches?
> >
> > Oops, I sent this email without my patch...
> >
> > ---
> > Subject: [PATCH] usb: otg: add hcd companion support
> >
> > 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 <[email protected]>
> > ---
> > Documentation/devicetree/bindings/usb/generic.txt | 3 +++
> > drivers/usb/common/usb-otg.c | 17 +++++++++++++----
> > include/linux/usb/otg.h | 7 ++++++-
> > 3 files changed, 22 insertions(+), 5 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 41e762a..83c8c96 100644
> > --- a/drivers/usb/common/usb-otg.c
> > +++ b/drivers/usb/common/usb-otg.c
> > @@ -20,6 +20,7 @@
> > #include <linux/list.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>
> > @@ -600,6 +601,10 @@ struct usb_otg *usb_otg_register(struct device *dev,
> > else
> > INIT_WORK(&otg->work, usb_otg_work);
> >
> > + if (of_find_property(dev->of_node, "hcd-needs-companion", NULL) ||
> > + config->hcd_needs_companion) /* needs comanion ? */
> > + otg->flags |= OTG_FLAG_HCD_NEEDS_COMPANION;
> > +
> > otg->wq = create_singlethread_workqueue("usb_otg");
> > if (!otg->wq) {
> > dev_err(dev, "otg: %s: can't create workqueue\n",
> > @@ -823,13 +828,15 @@ 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");
> > goto err;
> > @@ -865,7 +872,9 @@ 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 */
> >
> > diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h
> > index b094352..6f4ca77 100644
> > --- a/include/linux/usb/otg.h
> > +++ b/include/linux/usb/otg.h
> > @@ -57,7 +57,8 @@ struct otg_hcd {
> > * @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;
> > @@ -84,6 +85,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 -----------------------------*/
> > @@ -125,11 +127,14 @@ struct usb_otg_caps {
> > * @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);
> > --
>
> Hi Yoshihiro, the shared_hcd is used for USB3 only [1], this patch may
> not be suitable for supporting companion controller at OTG framework.
> For companion or other USB2 controllers, both primary hcd and shard hcd
> are NULL for HCD core.
>

Oh, I may be wrong. It should be ok for companion controller by using
this patch. Yoshihiro, do all below variables are NULL after both hcds
call usb_otg_register_hcd?

otg->primary_hcd.hcd->shared_hcd
otg->primary_hcd.hcd->primary_hcd
otg->shared_hcd.hcd->shared_hcd
otg->shared_hcd.hcd->primary_hcd

--

Best Regards,
Peter Chen

2016-04-22 05:58:06

by Yoshihiro Shimoda

[permalink] [raw]
Subject: RE: [PATCH v6 07/12] usb: otg: add OTG/dual-role core

Hi,

> From: Peter Chen
> Sent: Friday, April 22, 2016 12:34 PM
>
> On Fri, Apr 22, 2016 at 09:26:46AM +0800, Peter Chen wrote:
> > On Fri, Apr 15, 2016 at 10:03:16AM +0000, Yoshihiro Shimoda wrote:
> > > Hi,
> > >
> > > > From: Yoshihiro Shimoda
> > > > Sent: Friday, April 15, 2016 6:59 PM
> > > >
> > > > Hi,
> > > >
> > > > > From: Roger Quadros
> > > > > Sent: Thursday, April 14, 2016 8:32 PM
> > > > >
> > > > > On 14/04/16 14:15, Yoshihiro Shimoda wrote:
> > > > > > Hi,
> > > > > >
> > > > < snip >
> > > > > >>> @@ -865,7 +867,8 @@ 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->caps->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 */
> > > > > >>>
> > > > > >>
> > > > > >> These changes look good to me. Thanks.
> > > > > >
> > > > > > Thank you for the comment.
> > > > > > If we change the "needs_companion" place to the otg_config,
> > > > > > do we need to add a flag into the otg, instead of otg->caps?
> > > > >
> > > > > Yes we can add a flag in struct usb_otg.
> > > >
> > > > Thank you for the comment.
> > > >
> > > > I made a fixed patch.
> > > > So, should I send this patch to ML after you sent v7 patches?
> > > > Or, would you apply this patch before you send v7 patches?
> > >
> > > Oops, I sent this email without my patch...
> > >
> > > ---
> > > Subject: [PATCH] usb: otg: add hcd companion support
> > >
> > > 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 <[email protected]>
> > > ---
> > > Documentation/devicetree/bindings/usb/generic.txt | 3 +++
> > > drivers/usb/common/usb-otg.c | 17 +++++++++++++----
> > > include/linux/usb/otg.h | 7 ++++++-
> > > 3 files changed, 22 insertions(+), 5 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 41e762a..83c8c96 100644
> > > --- a/drivers/usb/common/usb-otg.c
> > > +++ b/drivers/usb/common/usb-otg.c
> > > @@ -20,6 +20,7 @@
> > > #include <linux/list.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>
> > > @@ -600,6 +601,10 @@ struct usb_otg *usb_otg_register(struct device *dev,
> > > else
> > > INIT_WORK(&otg->work, usb_otg_work);
> > >
> > > + if (of_find_property(dev->of_node, "hcd-needs-companion", NULL) ||
> > > + config->hcd_needs_companion) /* needs comanion ? */
> > > + otg->flags |= OTG_FLAG_HCD_NEEDS_COMPANION;
> > > +
> > > otg->wq = create_singlethread_workqueue("usb_otg");
> > > if (!otg->wq) {
> > > dev_err(dev, "otg: %s: can't create workqueue\n",
> > > @@ -823,13 +828,15 @@ 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");
> > > goto err;
> > > @@ -865,7 +872,9 @@ 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 */
> > >
> > > diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h
> > > index b094352..6f4ca77 100644
> > > --- a/include/linux/usb/otg.h
> > > +++ b/include/linux/usb/otg.h
> > > @@ -57,7 +57,8 @@ struct otg_hcd {
> > > * @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;
> > > @@ -84,6 +85,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 -----------------------------*/
> > > @@ -125,11 +127,14 @@ struct usb_otg_caps {
> > > * @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);
> > > --
> >
> > Hi Yoshihiro, the shared_hcd is used for USB3 only [1], this patch may
> > not be suitable for supporting companion controller at OTG framework.
> > For companion or other USB2 controllers, both primary hcd and shard hcd
> > are NULL for HCD core.
> >
>
> Oh, I may be wrong. It should be ok for companion controller by using
> this patch. Yoshihiro, do all below variables are NULL after both hcds
> call usb_otg_register_hcd?
>
> otg->primary_hcd.hcd->shared_hcd
> otg->primary_hcd.hcd->primary_hcd
> otg->shared_hcd.hcd->shared_hcd
> otg->shared_hcd.hcd->primary_hcd

Thank you for the review twice! :)
I checked these variables and the variables was set to NULL after both
hcds call usb_otg_register_hcd.

< a patch for the check >
diff --git a/drivers/usb/common/usb-otg.c b/drivers/usb/common/usb-otg.c
index 83c8c96..1fb7ea0 100644
--- a/drivers/usb/common/usb-otg.c
+++ b/drivers/usb/common/usb-otg.c
@@ -884,6 +884,19 @@ int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
dev_dbg(otg_dev, "otg: can't start till shared host registers\n");
}

+ if (otg->primary_hcd.hcd) {
+ dev_info(otg_dev, "primary_hcd.hcd->shared_hcd = %p\n",
+ otg->primary_hcd.hcd->shared_hcd);
+ dev_info(otg_dev, "primary_hcd.hcd->primary_hcd = %p\n",
+ otg->primary_hcd.hcd->primary_hcd);
+ }
+ if (otg->shared_hcd.hcd) {
+ dev_info(otg_dev, "shared_hcd.hcd->shared_hcd = %p\n",
+ otg->shared_hcd.hcd->shared_hcd);
+ dev_info(otg_dev, "shared_hcd.hcd->primary_hcd = %p\n",
+ otg->shared_hcd.hcd->primary_hcd);
+ }
+
mutex_unlock(&otg->fsm.lock);

return 0;

< result >
[ 1.797122] phy_rcar_gen3_usb2 ee080200.usb-phy: otg: limiting to dual-role
[ 1.804228] phy_rcar_gen3_usb2 ee080200.usb-phy: otg: primary host ee080100.usb registered
[ 1.812168] phy_rcar_gen3_usb2 ee080200.usb-phy: primary_hcd.hcd->shared_hcd = (null)
[ 1.820520] phy_rcar_gen3_usb2 ee080200.usb-phy: primary_hcd.hcd->primary_hcd = (null)
[ 1.829136] phy_rcar_gen3_usb2 ee080200.usb-phy: otg: shared host ee080000.usb registered
[ 1.836993] phy_rcar_gen3_usb2 ee080200.usb-phy: otg: can't start till gadget registers
[ 1.844675] phy_rcar_gen3_usb2 ee080200.usb-phy: primary_hcd.hcd->shared_hcd = (null)
[ 1.853024] phy_rcar_gen3_usb2 ee080200.usb-phy: primary_hcd.hcd->primary_hcd = (null)
[ 1.861458] phy_rcar_gen3_usb2 ee080200.usb-phy: shared_hcd.hcd->shared_hcd = (null)
[ 1.869724] phy_rcar_gen3_usb2 ee080200.usb-phy: shared_hcd.hcd->primary_hcd = (null)

Best regards,
Yoshihiro Shimoda

> --
>
> Best Regards,
> Peter Chen

2016-04-22 06:12:39

by Peter Chen

[permalink] [raw]
Subject: Re: [PATCH v6 07/12] usb: otg: add OTG/dual-role core

On Wed, Apr 20, 2016 at 10:03:34AM +0300, Roger Quadros wrote:
> On 20/04/16 08:08, Yoshihiro Shimoda wrote:
> > Hi,
> >
> >> From: Peter Chen
> >> Sent: Tuesday, April 19, 2016 6:18 PM
> >>
> >> On Fri, Apr 15, 2016 at 10:03:16AM +0000, Yoshihiro Shimoda wrote:
> >>> Hi,
> >>>
> >>>> From: Yoshihiro Shimoda
> >>>> Sent: Friday, April 15, 2016 6:59 PM
> >>>>
> >>>> Hi,
> >>>>
> >>>>> From: Roger Quadros
> >>>>> Sent: Thursday, April 14, 2016 8:32 PM
> >>>>>
> >>>>> On 14/04/16 14:15, Yoshihiro Shimoda wrote:
> >>>>>> Hi,
> >>>>>>
> >>>> < snip >
> >>>>>>>> @@ -865,7 +867,8 @@ 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->caps->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 */
> >>>>>>>>
> >>>>>>>
> >>>>>>> These changes look good to me. Thanks.
> >>>>>>
> >>>>>> Thank you for the comment.
> >>>>>> If we change the "needs_companion" place to the otg_config,
> >>>>>> do we need to add a flag into the otg, instead of otg->caps?
> >>>>>
> >>>>> Yes we can add a flag in struct usb_otg.
> >>>>
> >>>> Thank you for the comment.
> >>>>
> >>>> I made a fixed patch.
> >>>> So, should I send this patch to ML after you sent v7 patches?
> >>>> Or, would you apply this patch before you send v7 patches?
> >>>
> >>> Oops, I sent this email without my patch...
> >>>
> >>> ---
> >>> Subject: [PATCH] usb: otg: add hcd companion support
> >>>
> >>> 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 <[email protected]>
> >>> ---
> >>> Documentation/devicetree/bindings/usb/generic.txt | 3 +++
> >>> drivers/usb/common/usb-otg.c | 17 +++++++++++++----
> >>> include/linux/usb/otg.h | 7 ++++++-
> >>> 3 files changed, 22 insertions(+), 5 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 41e762a..83c8c96 100644
> >>> --- a/drivers/usb/common/usb-otg.c
> >>> +++ b/drivers/usb/common/usb-otg.c
> >>> @@ -20,6 +20,7 @@
> >>> #include <linux/list.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>
> >>> @@ -600,6 +601,10 @@ struct usb_otg *usb_otg_register(struct device *dev,
> >>> else
> >>> INIT_WORK(&otg->work, usb_otg_work);
> >>>
> >>> + if (of_find_property(dev->of_node, "hcd-needs-companion", NULL) ||
> >>> + config->hcd_needs_companion) /* needs comanion ? */
> >>
> >> %s/comanion/companion
> >
> > Thank you for pointing it out!
> >
> > Roger, would you fix this in your v7 patch set?
>
> Yes, I'll fix it locally. You don't need to post it again.
>

Roger, I am ok with this patch.

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

Peter
> --
> cheers,
> -roger
>
> >
> >> I have a little puzzled with companion controller and shared hcd, let me
> >> post a topic for it.
> >
> > I looked at the email thread.
> > It is very useful information to me! :)
> >
> > Best regards,
> > Yoshihiro Shimoda
> >
> >> Peter
> >>
> >>> + otg->flags |= OTG_FLAG_HCD_NEEDS_COMPANION;
> >>> +
> >>> otg->wq = create_singlethread_workqueue("usb_otg");
> >>> if (!otg->wq) {
> >>> dev_err(dev, "otg: %s: can't create workqueue\n",
> >>> @@ -823,13 +828,15 @@ 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");
> >>> goto err;
> >>> @@ -865,7 +872,9 @@ 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 */
> >>>
> >>> diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h
> >>> index b094352..6f4ca77 100644
> >>> --- a/include/linux/usb/otg.h
> >>> +++ b/include/linux/usb/otg.h
> >>> @@ -57,7 +57,8 @@ struct otg_hcd {
> >>> * @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;
> >>> @@ -84,6 +85,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 -----------------------------*/
> >>> @@ -125,11 +127,14 @@ struct usb_otg_caps {
> >>> * @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);
> >>> --
> >>> 1.9.1
> >>>
> >>> --
> >>> To unsubscribe from this list: send the line "unsubscribe linux-usb" in
> >>> the body of a message to [email protected]
> >>> More majordomo info at http://vger.kernel.org/majordomo-info.html
> >>
> >> --
> >>
> >> Best Regards,
> >> Peter Chen

--

Best Regards,
Peter Chen

2016-04-25 14:04:50

by Roger Quadros

[permalink] [raw]
Subject: Re: [PATCH v6 09/12] usb: gadget: udc: adapt to OTG core

Hi,

On 21/04/16 09:38, Jun Li wrote:
> Hi,
>
> ...
>>
>> /**
>> + * usb_gadget_start - start the usb gadget controller and connect to
>> +bus
>> + * @gadget: the gadget device to start
>> + *
>> + * This is external API for use by OTG core.
>> + *
>> + * Start the usb device controller and connect to bus (enable pull).
>> + */
>> +static int usb_gadget_start(struct usb_gadget *gadget) {
>> + int ret;
>> + struct usb_udc *udc = NULL;
>> +
>> + dev_dbg(&gadget->dev, "%s\n", __func__);
>> + mutex_lock(&udc_lock);
>> + list_for_each_entry(udc, &udc_list, list)
>> + if (udc->gadget == gadget)
>> + goto found;
>> +
>> + dev_err(gadget->dev.parent, "%s: gadget not registered.\n",
>> + __func__);
>> + mutex_unlock(&udc_lock);
>> + return -EINVAL;
>> +
>> +found:
>> + ret = usb_gadget_udc_start(udc);
>> + if (ret)
>> + dev_err(&udc->dev, "USB Device Controller didn't start: %d\n",
>> + ret);
>> + else
>> + usb_udc_connect_control(udc);
>
> For drd, it's fine, but for real otg, gadget connect should be done
> by loc_conn() instead of gadget start.

It is upto the OTG state machine to call gadget_start() when it needs to
connect to the bus (i.e. loc_conn()). I see no point in calling
gadget start before.

Do you see any issue in doing so?

cheers,
-roger

>
>> +
>> + mutex_unlock(&udc_lock);
>> +
>> + return ret;
>> +}
>> +
>> +/**
>> + * usb_gadget_stop - disconnect from bus and stop the usb gadget
>> + * @gadget: The gadget device we want to stop
>> + *
>> + * This is external API for use by OTG core.
>> + *
>> + * Disconnect from the bus (disable pull) and stop the
>> + * gadget controller.
>> + */
>> +static int usb_gadget_stop(struct usb_gadget *gadget) {
>> + struct usb_udc *udc = NULL;
>> +
>> + dev_dbg(&gadget->dev, "%s\n", __func__);
>> + mutex_lock(&udc_lock);
>> + list_for_each_entry(udc, &udc_list, list)
>> + if (udc->gadget == gadget)
>> + goto found;
>> +
>> + dev_err(gadget->dev.parent, "%s: gadget not registered.\n",
>> + __func__);
>> + mutex_unlock(&udc_lock);
>> + return -EINVAL;
>> +
>> +found:
>> + usb_gadget_disconnect(udc->gadget);
>
> Likewise, gadget disconnect also should be done by loc_conn()
> instead of gadget stop.
>
>> + udc->driver->disconnect(udc->gadget);
>> + usb_gadget_udc_stop(udc);
>> + mutex_unlock(&udc_lock);
>> +
>> + return 0;
>> +}
>> +
>
> Li Jun
>

2016-04-25 14:05:59

by Roger Quadros

[permalink] [raw]
Subject: Re: [PATCH v6 07/12] usb: otg: add OTG/dual-role core

Peter,

On 21/04/16 09:52, Peter Chen wrote:
> On Tue, Apr 05, 2016 at 05:05:12PM +0300, Roger Quadros wrote:
>> It provides APIs for the following tasks
>>
>> - Registering an OTG/dual-role capable controller
>> - Registering Host and Gadget controllers to OTG core
>> - Providing inputs to and kicking the OTG state machine
>>
>> Provide a dual-role device (DRD) state machine.
>> DRD mode is a reduced functionality OTG mode. In this mode
>> we don't support SRP, HNP and dynamic role-swap.
>>
>> In DRD operation, the controller mode (Host or Peripheral)
>> is decided based on the ID pin status. Once a cable plug (Type-A
>> or Type-B) is attached the controller selects the state
>> and doesn't change till the cable in unplugged and a different
>> cable type is inserted.
>>
>> As we don't need most of the complex OTG states and OTG timers
>> we implement a lean DRD state machine in usb-otg.c.
>> The DRD state machine is only interested in 2 hardware inputs
>> 'id' and 'b_sess_vld'.
>>
>> Signed-off-by: Roger Quadros <[email protected]>
>> ---
>> drivers/usb/common/Makefile | 2 +-
>> drivers/usb/common/usb-otg.c | 1058 ++++++++++++++++++++++++++++++++++++++++++
>> drivers/usb/common/usb-otg.h | 71 +++
>> drivers/usb/core/Kconfig | 2 +-
>> include/linux/usb/gadget.h | 2 +
>> include/linux/usb/hcd.h | 1 +
>> include/linux/usb/otg-fsm.h | 7 +
>> include/linux/usb/otg.h | 154 +++++-
>> 8 files changed, 1292 insertions(+), 5 deletions(-)
>> create mode 100644 drivers/usb/common/usb-otg.c
>> create mode 100644 drivers/usb/common/usb-otg.h
>>
>> diff --git a/drivers/usb/common/Makefile b/drivers/usb/common/Makefile
>> index f8f2c88..730d928 100644
>> --- a/drivers/usb/common/Makefile
>> +++ b/drivers/usb/common/Makefile
>> @@ -7,5 +7,5 @@ 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
>> +usbotg-y := usb-otg.o usb-otg-fsm.o
>> obj-$(CONFIG_USB_OTG) += usbotg.o
>> diff --git a/drivers/usb/common/usb-otg.c b/drivers/usb/common/usb-otg.c
>> new file mode 100644
>> index 0000000..41e762a
>> --- /dev/null
>> +++ b/drivers/usb/common/usb-otg.c
>> @@ -0,0 +1,1058 @@
>> +/**
>> + * drivers/usb/common/usb-otg.c - USB OTG core
>> + *
>> + * Copyright (C) 2016 Texas Instruments Incorporated - http://www.ti.com
>> + * Author: Roger Quadros <[email protected]>
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 as
>> + * published by the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>> + * GNU General Public License for more details.
>> + */
>> +
>> +#include <linux/kernel.h>
>> +#include <linux/ktime.h>
>> +#include <linux/hrtimer.h>
>> +#include <linux/list.h>
>> +#include <linux/of.h>
>> +#include <linux/of_platform.h>
>> +#include <linux/usb/otg.h>
>> +#include <linux/usb/gadget.h>
>> +#include <linux/workqueue.h>
>> +
>> +#include "usb-otg.h"
>> +
>> +struct otg_gcd {
>> + struct usb_gadget *gadget;
>> + struct otg_gadget_ops *ops;
>> +};
>> +
>> +/* OTG device list */
>> +LIST_HEAD(otg_list);
>> +static DEFINE_MUTEX(otg_list_mutex);
>> +
>> +/* Hosts and Gadgets waiting for OTG controller */
>> +struct otg_wait_data {
>> + struct device *dev; /* OTG controller device */
>> +
>> + struct otg_hcd primary_hcd;
>> + struct otg_hcd shared_hcd;
>> + struct otg_gcd gcd;
>> + struct list_head list;
>> +};
>> +
>> +LIST_HEAD(wait_list);
>> +static DEFINE_MUTEX(wait_list_mutex);
>> +
>> +static int usb_otg_hcd_is_primary_hcd(struct usb_hcd *hcd)
>> +{
>> + if (!hcd->primary_hcd)
>> + return 1;
>> + return hcd == hcd->primary_hcd;
>> +}
>
> Just find there is already a usb_hcd_is_primary_hcd at hcd.c,
> would you use it directly?

We can't even if we make it public as HCD can be a loadable module
and OTG is always built-in thus leading to undefined reference.

cheers,
-roger

>
> Peter
>> +
>> +/**
>> + * Check if the OTG device is in our wait list and return
>> + * otg_wait_data, else NULL.
>> + *
>> + * wait_list_mutex must be held.
>> + */
>> +static struct otg_wait_data *usb_otg_get_wait(struct device *otg_dev)
>> +{
>> + struct otg_wait_data *wait;
>> +
>> + if (!otg_dev)
>> + return NULL;
>> +
>> + /* is there an entry for this otg_dev ?*/
>> + list_for_each_entry(wait, &wait_list, list) {
>> + if (wait->dev == otg_dev)
>> + return wait;
>> + }
>> +
>> + return NULL;
>> +}
>> +
>> +/**
>> + * Add the hcd to our wait list
>> + */
>> +static int usb_otg_hcd_wait_add(struct device *otg_dev, struct usb_hcd *hcd,
>> + unsigned int irqnum, unsigned long irqflags,
>> + struct otg_hcd_ops *ops)
>> +{
>> + struct otg_wait_data *wait;
>> + int ret = -EINVAL;
>> +
>> + mutex_lock(&wait_list_mutex);
>> +
>> + wait = usb_otg_get_wait(otg_dev);
>> + if (!wait) {
>> + /* Not yet in wait list? allocate and add */
>> + wait = kzalloc(sizeof(*wait), GFP_KERNEL);
>> + if (!wait) {
>> + ret = -ENOMEM;
>> + goto fail;
>> + }
>> +
>> + wait->dev = otg_dev;
>> + list_add_tail(&wait->list, &wait_list);
>> + }
>> +
>> + if (usb_otg_hcd_is_primary_hcd(hcd)) {
>> + if (wait->primary_hcd.hcd) /* already assigned? */
>> + goto fail;
>> +
>> + wait->primary_hcd.hcd = hcd;
>> + wait->primary_hcd.irqnum = irqnum;
>> + wait->primary_hcd.irqflags = irqflags;
>> + wait->primary_hcd.ops = ops;
>> + wait->primary_hcd.otg_dev = otg_dev;
>> + } else {
>> + if (wait->shared_hcd.hcd) /* already assigned? */
>> + goto fail;
>> +
>> + wait->shared_hcd.hcd = hcd;
>> + wait->shared_hcd.irqnum = irqnum;
>> + wait->shared_hcd.irqflags = irqflags;
>> + wait->shared_hcd.ops = ops;
>> + wait->shared_hcd.otg_dev = otg_dev;
>> + }
>> +
>> + mutex_unlock(&wait_list_mutex);
>> + return 0;
>> +
>> +fail:
>> + mutex_unlock(&wait_list_mutex);
>> + return ret;
>> +}
>> +
>> +/**
>> + * Check and free wait list entry if empty
>> + *
>> + * wait_list_mutex must be held
>> + */
>> +static void usb_otg_check_free_wait(struct otg_wait_data *wait)
>> +{
>> + if (wait->primary_hcd.hcd || wait->shared_hcd.hcd || wait->gcd.gadget)
>> + return;
>> +
>> + list_del(&wait->list);
>> + kfree(wait);
>> +}
>> +
>> +/**
>> + * Remove the hcd from our wait list
>> + */
>> +static int usb_otg_hcd_wait_remove(struct usb_hcd *hcd)
>> +{
>> + struct otg_wait_data *wait;
>> +
>> + mutex_lock(&wait_list_mutex);
>> +
>> + /* is there an entry for this hcd ?*/
>> + list_for_each_entry(wait, &wait_list, list) {
>> + if (wait->primary_hcd.hcd == hcd) {
>> + wait->primary_hcd.hcd = 0;
>> + goto found;
>> + } else if (wait->shared_hcd.hcd == hcd) {
>> + wait->shared_hcd.hcd = 0;
>> + goto found;
>> + }
>> + }
>> +
>> + mutex_unlock(&wait_list_mutex);
>> + return -EINVAL;
>> +
>> +found:
>> + usb_otg_check_free_wait(wait);
>> + mutex_unlock(&wait_list_mutex);
>> +
>> + return 0;
>> +}
>> +
>> +/**
>> + * Add the gadget to our wait list
>> + */
>> +static int usb_otg_gadget_wait_add(struct device *otg_dev,
>> + struct usb_gadget *gadget,
>> + struct otg_gadget_ops *ops)
>> +{
>> + struct otg_wait_data *wait;
>> + int ret = -EINVAL;
>> +
>> + mutex_lock(&wait_list_mutex);
>> +
>> + wait = usb_otg_get_wait(otg_dev);
>> + if (!wait) {
>> + /* Not yet in wait list? allocate and add */
>> + wait = kzalloc(sizeof(*wait), GFP_KERNEL);
>> + if (!wait) {
>> + ret = -ENOMEM;
>> + goto fail;
>> + }
>> +
>> + wait->dev = otg_dev;
>> + list_add_tail(&wait->list, &wait_list);
>> + }
>> +
>> + if (wait->gcd.gadget) /* already assigned? */
>> + goto fail;
>> +
>> + wait->gcd.gadget = gadget;
>> + wait->gcd.ops = ops;
>> + mutex_unlock(&wait_list_mutex);
>> +
>> + return 0;
>> +
>> +fail:
>> + mutex_unlock(&wait_list_mutex);
>> + return ret;
>> +}
>> +
>> +/**
>> + * Remove the gadget from our wait list
>> + */
>> +static int usb_otg_gadget_wait_remove(struct usb_gadget *gadget)
>> +{
>> + struct otg_wait_data *wait;
>> +
>> + mutex_lock(&wait_list_mutex);
>> +
>> + /* is there an entry for this gadget ?*/
>> + list_for_each_entry(wait, &wait_list, list) {
>> + if (wait->gcd.gadget == gadget) {
>> + wait->gcd.gadget = 0;
>> + goto found;
>> + }
>> + }
>> +
>> + mutex_unlock(&wait_list_mutex);
>> +
>> + return -EINVAL;
>> +
>> +found:
>> + usb_otg_check_free_wait(wait);
>> + mutex_unlock(&wait_list_mutex);
>> +
>> + return 0;
>> +}
>> +
>> +/**
>> + * Register pending host/gadget and remove entry from wait list
>> + */
>> +static void usb_otg_flush_wait(struct device *otg_dev)
>> +{
>> + struct otg_wait_data *wait;
>> + struct otg_hcd *host;
>> + struct otg_gcd *gadget;
>> +
>> + mutex_lock(&wait_list_mutex);
>> +
>> + wait = usb_otg_get_wait(otg_dev);
>> + if (!wait)
>> + goto done;
>> +
>> + dev_dbg(otg_dev, "otg: registering pending host/gadget\n");
>> + gadget = &wait->gcd;
>> + if (gadget)
>> + usb_otg_register_gadget(gadget->gadget, gadget->ops);
>> +
>> + host = &wait->primary_hcd;
>> + if (host->hcd)
>> + usb_otg_register_hcd(host->hcd, host->irqnum, host->irqflags,
>> + host->ops);
>> +
>> + host = &wait->shared_hcd;
>> + if (host->hcd)
>> + usb_otg_register_hcd(host->hcd, host->irqnum, host->irqflags,
>> + host->ops);
>> +
>> + list_del(&wait->list);
>> + kfree(wait);
>> +
>> +done:
>> + mutex_unlock(&wait_list_mutex);
>> +}
>> +
>> +/**
>> + * Check if the OTG device is in our OTG list and return
>> + * usb_otg data, else NULL.
>> + *
>> + * otg_list_mutex must be held.
>> + */
>> +static struct usb_otg *usb_otg_get_data(struct device *otg_dev)
>> +{
>> + struct usb_otg *otg;
>> +
>> + if (!otg_dev)
>> + return NULL;
>> +
>> + list_for_each_entry(otg, &otg_list, list) {
>> + if (otg->dev == otg_dev)
>> + return otg;
>> + }
>> +
>> + return NULL;
>> +}
>> +
>> +/**
>> + * usb_otg_start_host - start/stop the host controller
>> + * @otg: usb_otg instance
>> + * @on: true to start, false to stop
>> + *
>> + * Start/stop the USB host controller. This function is meant
>> + * for use by the OTG controller driver.
>> + */
>> +int usb_otg_start_host(struct usb_otg *otg, int on)
>> +{
>> + struct otg_hcd_ops *hcd_ops = otg->hcd_ops;
>> +
>> + dev_dbg(otg->dev, "otg: %s %d\n", __func__, on);
>> + if (!otg->host) {
>> + WARN_ONCE(1, "otg: fsm running without host\n");
>> + return 0;
>> + }
>> +
>> + if (on) {
>> + if (otg->flags & OTG_FLAG_HOST_RUNNING)
>> + return 0;
>> +
>> + otg->flags |= OTG_FLAG_HOST_RUNNING;
>> +
>> + /* start host */
>> + hcd_ops->add(otg->primary_hcd.hcd, otg->primary_hcd.irqnum,
>> + otg->primary_hcd.irqflags);
>> + if (otg->shared_hcd.hcd) {
>> + hcd_ops->add(otg->shared_hcd.hcd,
>> + otg->shared_hcd.irqnum,
>> + otg->shared_hcd.irqflags);
>> + }
>> + } else {
>> + if (!(otg->flags & OTG_FLAG_HOST_RUNNING))
>> + return 0;
>> +
>> + otg->flags &= ~OTG_FLAG_HOST_RUNNING;
>> +
>> + /* stop host */
>> + if (otg->shared_hcd.hcd)
>> + hcd_ops->remove(otg->shared_hcd.hcd);
>> +
>> + hcd_ops->remove(otg->primary_hcd.hcd);
>> + }
>> +
>> + return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(usb_otg_start_host);
>> +
>> +/**
>> + * usb_otg_start_gadget - start/stop the gadget controller
>> + * @otg: usb_otg instance
>> + * @on: true to start, false to stop
>> + *
>> + * Start/stop the USB gadget controller. This function is meant
>> + * for use by the OTG controller driver.
>> + */
>> +int usb_otg_start_gadget(struct usb_otg *otg, int on)
>> +{
>> + struct usb_gadget *gadget = otg->gadget;
>> +
>> + dev_dbg(otg->dev, "otg: %s %d\n", __func__, on);
>> + if (!gadget) {
>> + WARN_ONCE(1, "otg: fsm running without gadget\n");
>> + return 0;
>> + }
>> +
>> + if (on) {
>> + if (otg->flags & OTG_FLAG_GADGET_RUNNING)
>> + return 0;
>> +
>> + otg->flags |= OTG_FLAG_GADGET_RUNNING;
>> + otg->gadget_ops->start(otg->gadget);
>> + } else {
>> + if (!(otg->flags & OTG_FLAG_GADGET_RUNNING))
>> + return 0;
>> +
>> + otg->flags &= ~OTG_FLAG_GADGET_RUNNING;
>> + otg->gadget_ops->stop(otg->gadget);
>> + }
>> +
>> + return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(usb_otg_start_gadget);
>> +
>> +/**
>> + * Change USB protocol when there is a protocol change.
>> + * fsm->lock must be held.
>> + */
>> +static int drd_set_protocol(struct otg_fsm *fsm, int protocol)
>> +{
>> + struct usb_otg *otg = container_of(fsm, struct usb_otg, fsm);
>> + int ret = 0;
>> +
>> + if (fsm->protocol != protocol) {
>> + dev_dbg(otg->dev, "otg: changing role fsm->protocol= %d; new protocol= %d\n",
>> + fsm->protocol, protocol);
>> + /* stop old protocol */
>> + if (fsm->protocol == PROTO_HOST)
>> + ret = otg_start_host(otg, 0);
>> + else if (fsm->protocol == PROTO_GADGET)
>> + 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);
>> + if (ret)
>> + return ret;
>> +
>> + fsm->protocol = protocol;
>> + return 0;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +/**
>> + * Called when entering a DRD state.
>> + * fsm->lock must be held.
>> + */
>> +static void drd_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state)
>> +{
>> + struct usb_otg *otg = container_of(fsm, struct usb_otg, fsm);
>> +
>> + if (otg->state == new_state)
>> + return;
>> +
>> + fsm->state_changed = 1;
>> + dev_dbg(otg->dev, "otg: set state: %s\n",
>> + usb_otg_state_string(new_state));
>> + switch (new_state) {
>> + case OTG_STATE_B_IDLE:
>> + drd_set_protocol(fsm, PROTO_UNDEF);
>> + otg_drv_vbus(otg, 0);
>> + break;
>> + case OTG_STATE_B_PERIPHERAL:
>> + drd_set_protocol(fsm, PROTO_GADGET);
>> + otg_drv_vbus(otg, 0);
>> + break;
>> + case OTG_STATE_A_HOST:
>> + drd_set_protocol(fsm, PROTO_HOST);
>> + otg_drv_vbus(otg, 1);
>> + break;
>> + 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(otg->dev, "%s: otg: invalid state: %s\n",
>> + __func__, usb_otg_state_string(new_state));
>> + break;
>> + }
>> +
>> + otg->state = new_state;
>> +}
>> +
>> +/**
>> + * DRD state change judgement
>> + *
>> + * For DRD we're only interested in some of the OTG states
>> + * i.e. OTG_STATE_B_IDLE: both peripheral and host are stopped
>> + * OTG_STATE_B_PERIPHERAL: peripheral active
>> + * OTG_STATE_A_HOST: host active
>> + * we're only interested in the following inputs
>> + * fsm->id, fsm->b_sess_vld
>> + */
>> +int drd_statemachine(struct usb_otg *otg)
>> +{
>> + struct otg_fsm *fsm = &otg->fsm;
>> + enum usb_otg_state state;
>> + int ret;
>> +
>> + mutex_lock(&fsm->lock);
>> +
>> + fsm->state_changed = 0;
>> + state = otg->state;
>> +
>> + switch (state) {
>> + case OTG_STATE_UNDEFINED:
>> + if (!fsm->id)
>> + drd_set_state(fsm, OTG_STATE_A_HOST);
>> + else if (fsm->id && fsm->b_sess_vld)
>> + drd_set_state(fsm, OTG_STATE_B_PERIPHERAL);
>> + else
>> + drd_set_state(fsm, OTG_STATE_B_IDLE);
>> + break;
>> + case OTG_STATE_B_IDLE:
>> + if (!fsm->id)
>> + drd_set_state(fsm, OTG_STATE_A_HOST);
>> + else if (fsm->b_sess_vld)
>> + drd_set_state(fsm, OTG_STATE_B_PERIPHERAL);
>> + break;
>> + case OTG_STATE_B_PERIPHERAL:
>> + if (!fsm->id)
>> + drd_set_state(fsm, OTG_STATE_A_HOST);
>> + else if (!fsm->b_sess_vld)
>> + drd_set_state(fsm, OTG_STATE_B_IDLE);
>> + break;
>> + case OTG_STATE_A_HOST:
>> + if (fsm->id && fsm->b_sess_vld)
>> + drd_set_state(fsm, OTG_STATE_B_PERIPHERAL);
>> + else if (fsm->id && !fsm->b_sess_vld)
>> + drd_set_state(fsm, OTG_STATE_B_IDLE);
>> + break;
>> +
>> + /* 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(otg->dev, "%s: otg: invalid usb-drd state: %s\n",
>> + __func__, usb_otg_state_string(state));
>> + drd_set_state(fsm, OTG_STATE_UNDEFINED);
>> + 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);
>> +
>> +/**
>> + * OTG FSM/DRD work function
>> + */
>> +static void usb_otg_work(struct work_struct *work)
>> +{
>> + struct usb_otg *otg = container_of(work, struct usb_otg, work);
>> +
>> + pm_runtime_get_sync(otg->dev);
>> + drd_statemachine(otg);
>> + pm_runtime_put_sync(otg->dev);
>> +}
>> +
>> +/**
>> + * usb_otg_register() - Register the OTG/dual-role device to OTG core
>> + * @dev: OTG/dual-role controller device.
>> + * @config: OTG configuration.
>> + *
>> + * Registers the OTG/dual-role controller device with the USB OTG core.
>> + *
>> + * Return: struct usb_otg * if success, ERR_PTR() if error.
>> + */
>> +struct usb_otg *usb_otg_register(struct device *dev,
>> + struct usb_otg_config *config)
>> +{
>> + struct usb_otg *otg;
>> + struct otg_wait_data *wait;
>> + int ret = 0;
>> +
>> + if (!dev || !config || !config->fsm_ops)
>> + return ERR_PTR(-EINVAL);
>> +
>> + /* already in list? */
>> + mutex_lock(&otg_list_mutex);
>> + if (usb_otg_get_data(dev)) {
>> + dev_err(dev, "otg: %s: device already in otg list\n",
>> + __func__);
>> + ret = -EINVAL;
>> + goto unlock;
>> + }
>> +
>> + /* allocate and add to list */
>> + otg = kzalloc(sizeof(*otg), GFP_KERNEL);
>> + if (!otg) {
>> + ret = -ENOMEM;
>> + goto unlock;
>> + }
>> +
>> + otg->dev = dev;
>> + otg->caps = config->otg_caps;
>> +
>> + if ((otg->caps->hnp_support || otg->caps->srp_support ||
>> + otg->caps->adp_support) && !config->otg_work)
>> + dev_info(dev, "otg: limiting to dual-role\n");
>> +
>> + if (config->otg_work) /* custom otg_work ? */
>> + INIT_WORK(&otg->work, config->otg_work);
>> + else
>> + INIT_WORK(&otg->work, usb_otg_work);
>> +
>> + otg->wq = create_singlethread_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);
>> +
>> + /* were we in wait list? */
>> + mutex_lock(&wait_list_mutex);
>> + wait = usb_otg_get_wait(dev);
>> + mutex_unlock(&wait_list_mutex);
>> + if (wait) {
>> + /* register pending host/gadget and flush from list */
>> + usb_otg_flush_wait(dev);
>> + }
>> +
>> + return otg;
>> +
>> +err_wq:
>> + kfree(otg);
>> +unlock:
>> + mutex_unlock(&otg_list_mutex);
>> + return ERR_PTR(ret);
>> +}
>> +EXPORT_SYMBOL_GPL(usb_otg_register);
>> +
>> +/**
>> + * usb_otg_unregister() - Unregister the OTG/dual-role device from USB OTG core
>> + * @dev: OTG controller device.
>> + *
>> + * Unregisters the OTG/dual-role controller device from USB OTG core.
>> + * Prevents unregistering till both the associated Host and Gadget controllers
>> + * have unregistered from the OTG core.
>> + *
>> + * Return: 0 on success, error value otherwise.
>> + */
>> +int usb_otg_unregister(struct device *dev)
>> +{
>> + struct usb_otg *otg;
>> +
>> + mutex_lock(&otg_list_mutex);
>> + otg = usb_otg_get_data(dev);
>> + if (!otg) {
>> + dev_err(dev, "otg: %s: device not in otg list\n",
>> + __func__);
>> + mutex_unlock(&otg_list_mutex);
>> + return -EINVAL;
>> + }
>> +
>> + /* prevent unregister till both host & gadget have unregistered */
>> + if (otg->host || otg->gadget) {
>> + dev_err(dev, "otg: %s: host/gadget still registered\n",
>> + __func__);
>> + return -EBUSY;
>> + }
>> +
>> + /* OTG FSM is halted when host/gadget unregistered */
>> + destroy_workqueue(otg->wq);
>> +
>> + /* remove from otg list */
>> + list_del(&otg->list);
>> + kfree(otg);
>> + mutex_unlock(&otg_list_mutex);
>> +
>> + return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(usb_otg_unregister);
>> +
>> +/**
>> + * start/kick the OTG FSM if we can
>> + * fsm->lock must be held
>> + */
>> +static void usb_otg_start_fsm(struct usb_otg *otg)
>> +{
>> + struct otg_fsm *fsm = &otg->fsm;
>> +
>> + if (fsm->running)
>> + goto kick_fsm;
>> +
>> + if (!otg->host) {
>> + dev_info(otg->dev, "otg: can't start till host registers\n");
>> + return;
>> + }
>> +
>> + if (!otg->gadget) {
>> + dev_info(otg->dev, "otg: can't start till gadget registers\n");
>> + return;
>> + }
>> +
>> + fsm->running = true;
>> +kick_fsm:
>> + queue_work(otg->wq, &otg->work);
>> +}
>> +
>> +/**
>> + * stop the OTG FSM. Stops Host & Gadget controllers as well.
>> + * fsm->lock must be held
>> + */
>> +static void usb_otg_stop_fsm(struct usb_otg *otg)
>> +{
>> + struct otg_fsm *fsm = &otg->fsm;
>> +
>> + if (!fsm->running)
>> + return;
>> +
>> + /* no more new events queued */
>> + fsm->running = false;
>> +
>> + flush_workqueue(otg->wq);
>> + otg->state = OTG_STATE_UNDEFINED;
>> +
>> + /* stop host/gadget immediately */
>> + if (fsm->protocol == PROTO_HOST)
>> + otg_start_host(otg, 0);
>> + else if (fsm->protocol == PROTO_GADGET)
>> + otg_start_gadget(otg, 0);
>> + fsm->protocol = PROTO_UNDEF;
>> +}
>> +
>> +/**
>> + * usb_otg_sync_inputs - Sync OTG inputs with the OTG state machine
>> + * @fsm: OTG FSM instance
>> + *
>> + * Used by the OTG driver to update the inputs to the OTG
>> + * state machine.
>> + *
>> + * Can be called in IRQ context.
>> + */
>> +void usb_otg_sync_inputs(struct usb_otg *otg)
>> +{
>> + /* Don't kick FSM till it has started */
>> + if (!otg->fsm.running)
>> + return;
>> +
>> + /* Kick FSM */
>> + queue_work(otg->wq, &otg->work);
>> +}
>> +EXPORT_SYMBOL_GPL(usb_otg_sync_inputs);
>> +
>> +/**
>> + * usb_otg_kick_fsm - Kick the OTG state machine
>> + * @otg_dev: OTG controller device
>> + *
>> + * Used by USB host/device stack to sync OTG related
>> + * events to the OTG state machine.
>> + * e.g. change in host_bus->b_hnp_enable, gadget->b_hnp_enable
>> + *
>> + * Returns: 0 on success, error value otherwise.
>> + */
>> +int usb_otg_kick_fsm(struct device *otg_dev)
>> +{
>> + struct usb_otg *otg;
>> +
>> + mutex_lock(&otg_list_mutex);
>> + otg = usb_otg_get_data(otg_dev);
>> + mutex_unlock(&otg_list_mutex);
>> + if (!otg) {
>> + dev_dbg(otg_dev, "otg: %s: invalid otg device\n",
>> + __func__);
>> + return -ENODEV;
>> + }
>> +
>> + usb_otg_sync_inputs(otg);
>> +
>> + return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(usb_otg_kick_fsm);
>> +
>> +/**
>> + * usb_otg_register_hcd - Register the host controller to OTG core
>> + * @hcd: host controller device
>> + * @irqnum: interrupt number
>> + * @irqflags: interrupt flags
>> + * @ops: HCD ops to interface with the HCD
>> + *
>> + * This is used by the USB Host stack to register the host controller
>> + * to the OTG core. Host controller must not be started by the
>> + * caller as it is left upto the OTG state machine to do so.
>> + * hcd->otg_dev must contain the related otg controller device.
>> + *
>> + * Returns: 0 on success, error value otherwise.
>> + */
>> +int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
>> + unsigned long irqflags, struct otg_hcd_ops *ops)
>> +{
>> + struct usb_otg *otg;
>> + struct device *hcd_dev = hcd->self.controller;
>> + struct device *otg_dev = hcd->otg_dev;
>> +
>> + if (!otg_dev)
>> + return -EINVAL;
>> +
>> + /* we're otg but otg controller might not yet be registered */
>> + mutex_lock(&otg_list_mutex);
>> + otg = usb_otg_get_data(otg_dev);
>> + mutex_unlock(&otg_list_mutex);
>> + if (!otg) {
>> + dev_dbg(hcd_dev,
>> + "otg: controller not yet registered. waiting..\n");
>> + /*
>> + * otg controller might register later. Put the hcd in
>> + * wait list and call us back when ready
>> + */
>> + if (usb_otg_hcd_wait_add(otg_dev, hcd, irqnum, irqflags, ops)) {
>> + dev_err(hcd_dev, "otg: failed to add hcd to wait list\n");
>> + return -EINVAL;
>> + }
>> +
>> + return 0;
>> + }
>> +
>> + /* 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) {
>> + /* are we in wait list? */
>> + if (!usb_otg_hcd_wait_remove(hcd))
>> + return 0;
>> +
>> + dev_dbg(hcd_dev, "otg: host wasn't registered with otg\n");
>> + return -EINVAL;
>> + }
>> +
>> + mutex_lock(&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 {
>> + dev_err(otg_dev, "otg: host %s wasn't registered with otg\n",
>> + dev_name(hcd_dev));
>> + mutex_unlock(&otg->fsm.lock);
>> + return -EINVAL;
>> + }
>> +
>> + /* stop FSM & Host */
>> + usb_otg_stop_fsm(otg);
>> + otg->host = NULL;
>> +
>> + mutex_unlock(&otg->fsm.lock);
>> +
>> + return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(usb_otg_unregister_hcd);
>> +
>> +/**
>> + * usb_otg_register_gadget - Register the gadget controller to OTG core
>> + * @gadget: gadget controller
>> + *
>> + * This is used by the USB gadget stack to register the gadget controller
>> + * to the OTG core. Gadget controller must not be started by the
>> + * caller as it is left upto the OTG state machine to do so.
>> + *
>> + * Gadget core must call this only when all resources required for
>> + * gadget controller to run are available.
>> + * i.e. gadget function driver is available.
>> + *
>> + * Returns: 0 on success, error value otherwise.
>> + */
>> +int usb_otg_register_gadget(struct usb_gadget *gadget,
>> + struct otg_gadget_ops *ops)
>> +{
>> + struct usb_otg *otg;
>> + struct device *gadget_dev = &gadget->dev;
>> + struct device *otg_dev = gadget->otg_dev;
>> +
>> + if (!otg_dev)
>> + return -EINVAL; /* we're definitely not OTG */
>> +
>> + /* we're otg but otg controller might not yet be registered */
>> + mutex_lock(&otg_list_mutex);
>> + otg = usb_otg_get_data(otg_dev);
>> + mutex_unlock(&otg_list_mutex);
>> + if (!otg) {
>> + dev_dbg(gadget_dev,
>> + "otg: controller not yet registered. waiting..\n");
>> + /*
>> + * otg controller might register later. Put the gadget in
>> + * wait list and call us back when ready
>> + */
>> + if (usb_otg_gadget_wait_add(otg_dev, gadget, ops)) {
>> + dev_err(gadget_dev,
>> + "otg: failed to add to gadget to wait list\n");
>> + return -EINVAL;
>> + }
>> +
>> + return 0;
>> + }
>> +
>> + 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));
>> +
>> + /* start FSM */
>> + usb_otg_start_fsm(otg);
>> + mutex_unlock(&otg->fsm.lock);
>> +
>> + return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(usb_otg_register_gadget);
>> +
>> +/**
>> + * usb_otg_unregister_gadget - Unregister the gadget controller from OTG core
>> + * @gadget: gadget controller
>> + *
>> + * This is used by the USB gadget stack to unregister the gadget controller
>> + * from the OTG core. Ensures that gadget controller is halted
>> + * on successful return.
>> + *
>> + * Returns: 0 on success, error value otherwise.
>> + */
>> +int usb_otg_unregister_gadget(struct usb_gadget *gadget)
>> +{
>> + struct usb_otg *otg;
>> + struct device *gadget_dev = &gadget->dev;
>> + struct device *otg_dev = gadget->otg_dev;
>> +
>> + if (!otg_dev)
>> + return -EINVAL;
>> +
>> + mutex_lock(&otg_list_mutex);
>> + otg = usb_otg_get_data(otg_dev);
>> + mutex_unlock(&otg_list_mutex);
>> + if (!otg) {
>> + /* are we in wait list? */
>> + if (!usb_otg_gadget_wait_remove(gadget))
>> + return 0;
>> +
>> + dev_dbg(gadget_dev, "otg: gadget wasn't registered with otg\n");
>> + return -EINVAL;
>> + }
>> +
>> + mutex_lock(&otg->fsm.lock);
>> + if (otg->gadget != gadget) {
>> + dev_err(otg_dev, "otg: gadget %s wasn't registered with otg\n",
>> + dev_name(&gadget->dev));
>> + mutex_unlock(&otg->fsm.lock);
>> + return -EINVAL;
>> + }
>> +
>> + /* Stop FSM & gadget */
>> + 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);
>> diff --git a/drivers/usb/common/usb-otg.h b/drivers/usb/common/usb-otg.h
>> new file mode 100644
>> index 0000000..2bf3fbf
>> --- /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) 2016 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 ae228d0..b468a9f 100644
>> --- a/drivers/usb/core/Kconfig
>> +++ b/drivers/usb/core/Kconfig
>> @@ -42,7 +42,7 @@ config USB_DYNAMIC_MINORS
>> If you are unsure about this, say N here.
>>
>> config USB_OTG
>> - bool "OTG support"
>> + bool "OTG/Dual-role support"
>> depends on PM
>> default n
>> help
>> diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h
>> index 8c0ae64..1878ae1 100644
>> --- a/include/linux/usb/gadget.h
>> +++ b/include/linux/usb/gadget.h
>> @@ -583,6 +583,7 @@ struct usb_gadget_ops {
>> * @out_epnum: last used out ep number
>> * @in_epnum: last used in ep number
>> * @otg_caps: OTG capabilities of this gadget.
>> + * @otg_dev: OTG controller device, if needs to be used with OTG core.
>> * @sg_supported: true if we can handle scatter-gather
>> * @is_otg: True if the USB device port uses a Mini-AB jack, so that the
>> * gadget driver must provide a USB OTG descriptor.
>> @@ -639,6 +640,7 @@ struct usb_gadget {
>> unsigned out_epnum;
>> unsigned in_epnum;
>> struct usb_otg_caps *otg_caps;
>> + struct device *otg_dev;
>>
>> unsigned sg_supported:1;
>> unsigned is_otg:1;
>> diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h
>> index 861ccaa..2017cd4 100644
>> --- a/include/linux/usb/hcd.h
>> +++ b/include/linux/usb/hcd.h
>> @@ -184,6 +184,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 36f0cf9..ba6755c 100644
>> --- a/include/linux/usb/otg-fsm.h
>> +++ b/include/linux/usb/otg-fsm.h
>> @@ -61,6 +61,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
>> @@ -133,6 +138,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 */
>> @@ -188,6 +194,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..b094352 100644
>> --- a/include/linux/usb/otg.h
>> +++ b/include/linux/usb/otg.h
>> @@ -10,10 +10,55 @@
>> #define __LINUX_USB_OTG_H
>>
>> #include <linux/phy/phy.h>
>> -#include <linux/usb/phy.h>
>> -#include <linux/usb/otg-fsm.h>
>> +#include <linux/device.h>
>> +#include <linux/hrtimer.h>
>> +#include <linux/ktime.h>
>> +#include <linux/usb.h>
>> #include <linux/usb/hcd.h>
>> +#include <linux/usb/gadget.h>
>> +#include <linux/usb/otg-fsm.h>
>> +#include <linux/usb/phy.h>
>>
>> +/**
>> + * struct otg_hcd - host controller state and interface
>> + *
>> + * @hcd: host controller
>> + * @irqnum: irq number
>> + * @irqflags: irq flags
>> + * @ops: otg to host controller interface
>> + * @ops: otg to host controller interface
>> + * @otg_dev: otg controller device
>> + */
>> +struct otg_hcd {
>> + struct usb_hcd *hcd;
>> + unsigned int irqnum;
>> + unsigned long irqflags;
>> + struct otg_hcd_ops *ops;
>> + struct device *otg_dev;
>> +};
>> +
>> +/**
>> + * struct usb_otg - 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 +69,24 @@ 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;
>> + 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,7 +102,7 @@ struct usb_otg {
>>
>> /* start or continue HNP role switch */
>> int (*start_hnp)(struct usb_otg *otg);
>> -
>> +/*---------------------------------------------------------------*/
>> };
>>
>> /**
>> @@ -60,8 +120,92 @@ struct usb_otg_caps {
>> bool adp_support;
>> };
>>
>> +/**
>> + * struct usb_otg_config - otg controller configuration
>> + * @caps: otg capabilities of the controller
>> + * @ops: otg fsm operations
>> + * @otg_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);
>> +};
>> +
>> 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);
>> +
>> +#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;
>> +}
>> +#endif /* CONFIG_USB_OTG */
>> +
>> +/*------------- deprecated interface -----------------------------*/
>> /* Context: can sleep */
>> static inline int
>> otg_start_hnp(struct usb_otg *otg)
>> @@ -113,6 +257,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 +383,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.5.0
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-usb" in
>> the body of a message to [email protected]
>> More majordomo info at http://vger.kernel.org/majordomo-info.html
>

2016-04-26 00:07:12

by Jun Li

[permalink] [raw]
Subject: RE: [PATCH v6 09/12] usb: gadget: udc: adapt to OTG core

Hi

> -----Original Message-----
> From: Roger Quadros [mailto:[email protected]]
> Sent: Monday, April 25, 2016 10:04 PM
> To: Jun Li <[email protected]>; [email protected]; [email protected];
> [email protected]; [email protected]
> Cc: [email protected]; [email protected];
> [email protected]; [email protected]; [email protected];
> [email protected]; [email protected]; [email protected];
> [email protected]; [email protected]
> Subject: Re: [PATCH v6 09/12] usb: gadget: udc: adapt to OTG core
>
> Hi,
>
> On 21/04/16 09:38, Jun Li wrote:
> > Hi,
> >
> > ...
> >>
> >> /**
> >> + * usb_gadget_start - start the usb gadget controller and connect to
> >> +bus
> >> + * @gadget: the gadget device to start
> >> + *
> >> + * This is external API for use by OTG core.
> >> + *
> >> + * Start the usb device controller and connect to bus (enable pull).
> >> + */
> >> +static int usb_gadget_start(struct usb_gadget *gadget) {
> >> + int ret;
> >> + struct usb_udc *udc = NULL;
> >> +
> >> + dev_dbg(&gadget->dev, "%s\n", __func__);
> >> + mutex_lock(&udc_lock);
> >> + list_for_each_entry(udc, &udc_list, list)
> >> + if (udc->gadget == gadget)
> >> + goto found;
> >> +
> >> + dev_err(gadget->dev.parent, "%s: gadget not registered.\n",
> >> + __func__);
> >> + mutex_unlock(&udc_lock);
> >> + return -EINVAL;
> >> +
> >> +found:
> >> + ret = usb_gadget_udc_start(udc);
> >> + if (ret)
> >> + dev_err(&udc->dev, "USB Device Controller didn't start: %d\n",
> >> + ret);
> >> + else
> >> + usb_udc_connect_control(udc);
> >
> > For drd, it's fine, but for real otg, gadget connect should be done by
> > loc_conn() instead of gadget start.
>
> It is upto the OTG state machine to call gadget_start() when it needs to
> connect to the bus (i.e. loc_conn()). I see no point in calling gadget
> start before.
>
> Do you see any issue in doing so?

This is what OTG state machine does:
case OTG_STATE_B_PERIPHERAL:
otg_chrg_vbus(otg, 0);
otg_loc_sof(otg, 0);
otg_set_protocol(fsm, PROTO_GADGET);
otg_loc_conn(otg, 1);
break;

You intend to abstract something common in this api when start gadget,
which should be called by otg_set_protocol(fsm, PROTO_GADGET); and
drd_set_protocol(fsm, PROTO_GADGET); right?

So you may move usb_udc_connect_control(IMO usb_gadget_connect()
is better)out of usb_gadget_start(), then for drd:

case OTG_STATE_B_PERIPHERAL:
drd_set_protocol(fsm, PROTO_GADGET);
otg_drv_vbus(otg, 0);
usb_gadget_connect();

Li Jun

>
> cheers,
> -roger
>
> >
> >> +
> >> + mutex_unlock(&udc_lock);
> >> +
> >> + return ret;
> >> +}
> >> +
> >> +/**
> >> + * usb_gadget_stop - disconnect from bus and stop the usb gadget
> >> + * @gadget: The gadget device we want to stop
> >> + *
> >> + * This is external API for use by OTG core.
> >> + *
> >> + * Disconnect from the bus (disable pull) and stop the
> >> + * gadget controller.
> >> + */
> >> +static int usb_gadget_stop(struct usb_gadget *gadget) {
> >> + struct usb_udc *udc = NULL;
> >> +
> >> + dev_dbg(&gadget->dev, "%s\n", __func__);
> >> + mutex_lock(&udc_lock);
> >> + list_for_each_entry(udc, &udc_list, list)
> >> + if (udc->gadget == gadget)
> >> + goto found;
> >> +
> >> + dev_err(gadget->dev.parent, "%s: gadget not registered.\n",
> >> + __func__);
> >> + mutex_unlock(&udc_lock);
> >> + return -EINVAL;
> >> +
> >> +found:
> >> + usb_gadget_disconnect(udc->gadget);
> >
> > Likewise, gadget disconnect also should be done by loc_conn() instead
> > of gadget stop.
> >
> >> + udc->driver->disconnect(udc->gadget);
> >> + usb_gadget_udc_stop(udc);
> >> + mutex_unlock(&udc_lock);
> >> +
> >> + return 0;
> >> +}
> >> +
> >
> > Li Jun
> >

2016-04-26 02:08:03

by Jun Li

[permalink] [raw]
Subject: RE: [PATCH v6 07/12] usb: otg: add OTG/dual-role core

Hi Roger

> -----Original Message-----
> From: Roger Quadros [mailto:[email protected]]
> Sent: Tuesday, April 05, 2016 10:05 PM
> To: [email protected]; [email protected];
> [email protected]; [email protected]
> Cc: [email protected]; [email protected];
> [email protected]; [email protected]; [email protected];
> [email protected]; [email protected]; [email protected];
> [email protected]; [email protected]; Roger Quadros
> <[email protected]>
> Subject: [PATCH v6 07/12] usb: otg: add OTG/dual-role core
>
> 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 <[email protected]>
> ---

...

> +/**
> + * Register pending host/gadget and remove entry from wait list */
> +static void usb_otg_flush_wait(struct device *otg_dev) {
> + struct otg_wait_data *wait;
> + struct otg_hcd *host;
> + struct otg_gcd *gadget;
> +
> + mutex_lock(&wait_list_mutex);
> +
> + wait = usb_otg_get_wait(otg_dev);
> + if (!wait)
> + goto done;
> +
> + dev_dbg(otg_dev, "otg: registering pending host/gadget\n");
> + gadget = &wait->gcd;
> + if (gadget)

If (gadget->gadget)

> + usb_otg_register_gadget(gadget->gadget, gadget->ops);
> +
> + host = &wait->primary_hcd;
> + if (host->hcd)
> + usb_otg_register_hcd(host->hcd, host->irqnum, host->irqflags,
> + host->ops);
> +
> + host = &wait->shared_hcd;
> + if (host->hcd)
> + usb_otg_register_hcd(host->hcd, host->irqnum, host->irqflags,
> + host->ops);
> +
> + list_del(&wait->list);
> + kfree(wait);
> +
> +done:
> + mutex_unlock(&wait_list_mutex);
> +}
> +
> +/**
> + * Check if the OTG device is in our OTG list and return
> + * usb_otg data, else NULL.
> + *
> + * otg_list_mutex must be held.
> + */
> +static struct usb_otg *usb_otg_get_data(struct device *otg_dev) {
> + struct usb_otg *otg;
> +
> + if (!otg_dev)
> + return NULL;
> +
> + list_for_each_entry(otg, &otg_list, list) {
> + if (otg->dev == otg_dev)
> + return otg;
> + }
> +
> + return NULL;
> +}

Could you export it to be a public API, we may need access usb_otg
in common host driver for handling of enumeration of otg test device.

...

> +/**
> + * Called when entering a DRD state.
> + * fsm->lock must be held.
> + */
> +static void drd_set_state(struct otg_fsm *fsm, enum usb_otg_state
> +new_state) {
> + struct usb_otg *otg = container_of(fsm, struct usb_otg, fsm);
> +
> + if (otg->state == new_state)
> + return;
> +
> + fsm->state_changed = 1;
> + dev_dbg(otg->dev, "otg: set state: %s\n",
> + usb_otg_state_string(new_state));
> + switch (new_state) {
> + case OTG_STATE_B_IDLE:
> + drd_set_protocol(fsm, PROTO_UNDEF);
> + otg_drv_vbus(otg, 0);
> + break;
> + case OTG_STATE_B_PERIPHERAL:
> + drd_set_protocol(fsm, PROTO_GADGET);
> + otg_drv_vbus(otg, 0);
> + break;
> + case OTG_STATE_A_HOST:
> + drd_set_protocol(fsm, PROTO_HOST);
> + otg_drv_vbus(otg, 1);
> + break;
> + 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:

Remove above unused states.

> + default:
> + dev_warn(otg->dev, "%s: otg: invalid state: %s\n",
> + __func__, usb_otg_state_string(new_state));
> + break;
> + }
> +
> + otg->state = new_state;
> +}
> +
> +/**
> + * DRD state change judgement
> + *
> + * For DRD we're only interested in some of the OTG states
> + * i.e. OTG_STATE_B_IDLE: both peripheral and host are stopped
> + * OTG_STATE_B_PERIPHERAL: peripheral active
> + * OTG_STATE_A_HOST: host active
> + * we're only interested in the following inputs
> + * fsm->id, fsm->b_sess_vld
> + */
> +int drd_statemachine(struct usb_otg *otg) {
> + struct otg_fsm *fsm = &otg->fsm;
> + enum usb_otg_state state;
> + int ret;
> +
> + mutex_lock(&fsm->lock);
> +
> + fsm->state_changed = 0;
> + state = otg->state;
> +
> + switch (state) {
> + case OTG_STATE_UNDEFINED:
> + if (!fsm->id)
> + drd_set_state(fsm, OTG_STATE_A_HOST);
> + else if (fsm->id && fsm->b_sess_vld)
> + drd_set_state(fsm, OTG_STATE_B_PERIPHERAL);
> + else
> + drd_set_state(fsm, OTG_STATE_B_IDLE);
> + break;
> + case OTG_STATE_B_IDLE:
> + if (!fsm->id)
> + drd_set_state(fsm, OTG_STATE_A_HOST);
> + else if (fsm->b_sess_vld)
> + drd_set_state(fsm, OTG_STATE_B_PERIPHERAL);
> + break;
> + case OTG_STATE_B_PERIPHERAL:
> + if (!fsm->id)
> + drd_set_state(fsm, OTG_STATE_A_HOST);
> + else if (!fsm->b_sess_vld)
> + drd_set_state(fsm, OTG_STATE_B_IDLE);
> + break;
> + case OTG_STATE_A_HOST:
> + if (fsm->id && fsm->b_sess_vld)
> + drd_set_state(fsm, OTG_STATE_B_PERIPHERAL);
> + else if (fsm->id && !fsm->b_sess_vld)
> + drd_set_state(fsm, OTG_STATE_B_IDLE);
> + break;
> +
> + /* 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:

Remove above unused states and add a default:

> + dev_err(otg->dev, "%s: otg: invalid usb-drd state: %s\n",
> + __func__, usb_otg_state_string(state));
> + drd_set_state(fsm, OTG_STATE_UNDEFINED);
> + 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);
> +
> +/**
> + * OTG FSM/DRD work function

DRD work function

> + */
> +static void usb_otg_work(struct work_struct *work) {

usb_drd_work() name is better as it's only for drd.

> + struct usb_otg *otg = container_of(work, struct usb_otg, work);
> +
> + pm_runtime_get_sync(otg->dev);
> + drd_statemachine(otg);
> + pm_runtime_put_sync(otg->dev);
> +}
> +
> +/**
> + * usb_otg_register() - Register the OTG/dual-role device to OTG core
> + * @dev: OTG/dual-role controller device.
> + * @config: OTG configuration.
> + *
> + * Registers the OTG/dual-role controller device with the USB OTG core.
> + *
> + * Return: struct usb_otg * if success, ERR_PTR() if error.
> + */
> +struct usb_otg *usb_otg_register(struct device *dev,
> + struct usb_otg_config *config)
> +{
> + struct usb_otg *otg;
> + struct otg_wait_data *wait;
> + int ret = 0;
> +
> + if (!dev || !config || !config->fsm_ops)
> + return ERR_PTR(-EINVAL);
> +
> + /* already in list? */
> + mutex_lock(&otg_list_mutex);
> + if (usb_otg_get_data(dev)) {
> + dev_err(dev, "otg: %s: device already in otg list\n",
> + __func__);
> + ret = -EINVAL;
> + goto unlock;
> + }
> +
> + /* allocate and add to list */
> + otg = kzalloc(sizeof(*otg), GFP_KERNEL);
> + if (!otg) {
> + ret = -ENOMEM;
> + goto unlock;
> + }
> +
> + otg->dev = dev;
> + otg->caps = config->otg_caps;
> +
> + if ((otg->caps->hnp_support || otg->caps->srp_support ||
> + otg->caps->adp_support) && !config->otg_work)
> + dev_info(dev, "otg: limiting to dual-role\n");

dev_err, this should be an error.

> +
> + if (config->otg_work) /* custom otg_work ? */
> + INIT_WORK(&otg->work, config->otg_work);
> + else
> + INIT_WORK(&otg->work, usb_otg_work);
> +
> + otg->wq = create_singlethread_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);
> +
> + /* were we in wait list? */
> + mutex_lock(&wait_list_mutex);
> + wait = usb_otg_get_wait(dev);
> + mutex_unlock(&wait_list_mutex);
> + if (wait) {
> + /* register pending host/gadget and flush from list */
> + usb_otg_flush_wait(dev);
> + }
> +
> + return otg;
> +
> +err_wq:
> + kfree(otg);
> +unlock:
> + mutex_unlock(&otg_list_mutex);
> + return ERR_PTR(ret);
> +}
> +EXPORT_SYMBOL_GPL(usb_otg_register);
> +

2016-04-26 03:47:31

by Peter Chen

[permalink] [raw]
Subject: Re: [PATCH v6 07/12] usb: otg: add OTG/dual-role core

On Tue, Apr 26, 2016 at 02:07:56AM +0000, Jun Li wrote:
> > +struct usb_otg *usb_otg_register(struct device *dev,
> > + struct usb_otg_config *config)
> > +{
> > + struct usb_otg *otg;
> > + struct otg_wait_data *wait;
> > + int ret = 0;
> > +
> > + if (!dev || !config || !config->fsm_ops)
> > + return ERR_PTR(-EINVAL);
> > +
> > + /* already in list? */
> > + mutex_lock(&otg_list_mutex);
> > + if (usb_otg_get_data(dev)) {
> > + dev_err(dev, "otg: %s: device already in otg list\n",
> > + __func__);
> > + ret = -EINVAL;
> > + goto unlock;
> > + }
> > +
> > + /* allocate and add to list */
> > + otg = kzalloc(sizeof(*otg), GFP_KERNEL);
> > + if (!otg) {
> > + ret = -ENOMEM;
> > + goto unlock;
> > + }
> > +
> > + otg->dev = dev;
> > + otg->caps = config->otg_caps;
> > +
> > + if ((otg->caps->hnp_support || otg->caps->srp_support ||
> > + otg->caps->adp_support) && !config->otg_work)
> > + dev_info(dev, "otg: limiting to dual-role\n");
>
> dev_err, this should be an error.

The condition may be wrong, but it is an information to show
that current OTG is dual-role.

Peter
>
> > +
> > + if (config->otg_work) /* custom otg_work ? */
> > + INIT_WORK(&otg->work, config->otg_work);
> > + else
> > + INIT_WORK(&otg->work, usb_otg_work);
> > +
> > + otg->wq = create_singlethread_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);
> > +
> > + /* were we in wait list? */
> > + mutex_lock(&wait_list_mutex);
> > + wait = usb_otg_get_wait(dev);
> > + mutex_unlock(&wait_list_mutex);
> > + if (wait) {
> > + /* register pending host/gadget and flush from list */
> > + usb_otg_flush_wait(dev);
> > + }
> > +
> > + return otg;
> > +
> > +err_wq:
> > + kfree(otg);
> > +unlock:
> > + mutex_unlock(&otg_list_mutex);
> > + return ERR_PTR(ret);
> > +}
> > +EXPORT_SYMBOL_GPL(usb_otg_register);
> > +
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-usb" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html

--
Best Regards,
Peter Chen

2016-04-26 05:11:49

by Jun Li

[permalink] [raw]
Subject: RE: [PATCH v6 07/12] usb: otg: add OTG/dual-role core

Hi

> -----Original Message-----
> From: Peter Chen [mailto:[email protected]]
> Sent: Tuesday, April 26, 2016 11:47 AM
> To: Jun Li <[email protected]>
> Cc: Roger Quadros <[email protected]>; [email protected];
> [email protected]; [email protected]; [email protected];
> [email protected]; [email protected];
> [email protected]; [email protected]; [email protected];
> [email protected]; [email protected]; [email protected];
> [email protected]; [email protected]
> Subject: Re: [PATCH v6 07/12] usb: otg: add OTG/dual-role core
>
> On Tue, Apr 26, 2016 at 02:07:56AM +0000, Jun Li wrote:
> > > +struct usb_otg *usb_otg_register(struct device *dev,
> > > + struct usb_otg_config *config) {
> > > + struct usb_otg *otg;
> > > + struct otg_wait_data *wait;
> > > + int ret = 0;
> > > +
> > > + if (!dev || !config || !config->fsm_ops)
> > > + return ERR_PTR(-EINVAL);
> > > +
> > > + /* already in list? */
> > > + mutex_lock(&otg_list_mutex);
> > > + if (usb_otg_get_data(dev)) {
> > > + dev_err(dev, "otg: %s: device already in otg list\n",
> > > + __func__);
> > > + ret = -EINVAL;
> > > + goto unlock;
> > > + }
> > > +
> > > + /* allocate and add to list */
> > > + otg = kzalloc(sizeof(*otg), GFP_KERNEL);
> > > + if (!otg) {
> > > + ret = -ENOMEM;
> > > + goto unlock;
> > > + }
> > > +
> > > + otg->dev = dev;
> > > + otg->caps = config->otg_caps;
> > > +
> > > + if ((otg->caps->hnp_support || otg->caps->srp_support ||
> > > + otg->caps->adp_support) && !config->otg_work)
> > > + dev_info(dev, "otg: limiting to dual-role\n");
> >
> > dev_err, this should be an error.
>
> The condition may be wrong, but it is an information to show that current
> OTG is dual-role.

This should not happen in any correct design, I even doubt if we
should try to continue by "downgrade" it to be duel role, currently
the only example user is dual role, so doing like this can't be
tested by real case, this downgrade is not so easy like we image,
at least for chipidea otg driver, simply replace a queue worker may
not work, as we have much more difference between the 2 configs.

Li Jun

>
> Peter
> >
> > > +
> > > + if (config->otg_work) /* custom otg_work ? */
> > > + INIT_WORK(&otg->work, config->otg_work);
> > > + else
> > > + INIT_WORK(&otg->work, usb_otg_work);
> > > +
> > > + otg->wq = create_singlethread_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);
> > > +
> > > + /* were we in wait list? */
> > > + mutex_lock(&wait_list_mutex);
> > > + wait = usb_otg_get_wait(dev);
> > > + mutex_unlock(&wait_list_mutex);
> > > + if (wait) {
> > > + /* register pending host/gadget and flush from list */
> > > + usb_otg_flush_wait(dev);
> > > + }
> > > +
> > > + return otg;
> > > +
> > > +err_wq:
> > > + kfree(otg);
> > > +unlock:
> > > + mutex_unlock(&otg_list_mutex);
> > > + return ERR_PTR(ret);
> > > +}
> > > +EXPORT_SYMBOL_GPL(usb_otg_register);
> > > +
> >
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-usb"
> > in the body of a message to [email protected] More majordomo
> > info at http://vger.kernel.org/majordomo-info.html
>
> --
> Best Regards,
> Peter Chen

2016-04-26 06:28:24

by Peter Chen

[permalink] [raw]
Subject: Re: [PATCH v6 07/12] usb: otg: add OTG/dual-role core

On Tue, Apr 26, 2016 at 05:11:36AM +0000, Jun Li wrote:
> Hi
>
> > -----Original Message-----
> > From: Peter Chen [mailto:[email protected]]
> > Sent: Tuesday, April 26, 2016 11:47 AM
> > To: Jun Li <[email protected]>
> > Cc: Roger Quadros <[email protected]>; [email protected];
> > [email protected]; [email protected]; [email protected];
> > [email protected]; [email protected];
> > [email protected]; [email protected]; [email protected];
> > [email protected]; [email protected]; [email protected];
> > [email protected]; [email protected]
> > Subject: Re: [PATCH v6 07/12] usb: otg: add OTG/dual-role core
> >
> > On Tue, Apr 26, 2016 at 02:07:56AM +0000, Jun Li wrote:
> > > > +struct usb_otg *usb_otg_register(struct device *dev,
> > > > + struct usb_otg_config *config) {
> > > > + struct usb_otg *otg;
> > > > + struct otg_wait_data *wait;
> > > > + int ret = 0;
> > > > +
> > > > + if (!dev || !config || !config->fsm_ops)
> > > > + return ERR_PTR(-EINVAL);
> > > > +
> > > > + /* already in list? */
> > > > + mutex_lock(&otg_list_mutex);
> > > > + if (usb_otg_get_data(dev)) {
> > > > + dev_err(dev, "otg: %s: device already in otg list\n",
> > > > + __func__);
> > > > + ret = -EINVAL;
> > > > + goto unlock;
> > > > + }
> > > > +
> > > > + /* allocate and add to list */
> > > > + otg = kzalloc(sizeof(*otg), GFP_KERNEL);
> > > > + if (!otg) {
> > > > + ret = -ENOMEM;
> > > > + goto unlock;
> > > > + }
> > > > +
> > > > + otg->dev = dev;
> > > > + otg->caps = config->otg_caps;
> > > > +
> > > > + if ((otg->caps->hnp_support || otg->caps->srp_support ||
> > > > + otg->caps->adp_support) && !config->otg_work)
> > > > + dev_info(dev, "otg: limiting to dual-role\n");
> > >
> > > dev_err, this should be an error.
> >
> > The condition may be wrong, but it is an information to show that current
> > OTG is dual-role.
>
> This should not happen in any correct design, I even doubt if we
> should try to continue by "downgrade" it to be duel role, currently
> the only example user is dual role, so doing like this can't be
> tested by real case, this downgrade is not so easy like we image,
> at least for chipidea otg driver, simply replace a queue worker may
> not work, as we have much more difference between the 2 configs.
>

Would you show more why chipidea can't work just replace the work item,
and see if anything we still can improve for this framework?

--
Best Regards,
Peter Chen

2016-04-26 07:00:29

by Jun Li

[permalink] [raw]
Subject: RE: [PATCH v6 07/12] usb: otg: add OTG/dual-role core

Hi

> -----Original Message-----
> From: Peter Chen [mailto:[email protected]]
> Sent: Tuesday, April 26, 2016 2:28 PM
> To: Jun Li <[email protected]>
> Cc: Roger Quadros <[email protected]>; [email protected];
> [email protected]; [email protected]; [email protected];
> [email protected]; [email protected];
> [email protected]; [email protected]; [email protected];
> [email protected]; [email protected]; [email protected];
> [email protected]; [email protected]
> Subject: Re: [PATCH v6 07/12] usb: otg: add OTG/dual-role core
>
> On Tue, Apr 26, 2016 at 05:11:36AM +0000, Jun Li wrote:
> > Hi
> >
> > > -----Original Message-----
> > > From: Peter Chen [mailto:[email protected]]
> > > Sent: Tuesday, April 26, 2016 11:47 AM
> > > To: Jun Li <[email protected]>
> > > Cc: Roger Quadros <[email protected]>; [email protected];
> > > [email protected]; [email protected];
> > > [email protected]; [email protected];
> > > [email protected]; [email protected];
> > > [email protected]; [email protected]; [email protected];
> > > [email protected]; [email protected];
> > > [email protected]; [email protected]
> > > Subject: Re: [PATCH v6 07/12] usb: otg: add OTG/dual-role core
> > >
> > > On Tue, Apr 26, 2016 at 02:07:56AM +0000, Jun Li wrote:
> > > > > +struct usb_otg *usb_otg_register(struct device *dev,
> > > > > + struct usb_otg_config *config) {
> > > > > + struct usb_otg *otg;
> > > > > + struct otg_wait_data *wait;
> > > > > + int ret = 0;
> > > > > +
> > > > > + if (!dev || !config || !config->fsm_ops)
> > > > > + return ERR_PTR(-EINVAL);
> > > > > +
> > > > > + /* already in list? */
> > > > > + mutex_lock(&otg_list_mutex);
> > > > > + if (usb_otg_get_data(dev)) {
> > > > > + dev_err(dev, "otg: %s: device already in otg list\n",
> > > > > + __func__);
> > > > > + ret = -EINVAL;
> > > > > + goto unlock;
> > > > > + }
> > > > > +
> > > > > + /* allocate and add to list */
> > > > > + otg = kzalloc(sizeof(*otg), GFP_KERNEL);
> > > > > + if (!otg) {
> > > > > + ret = -ENOMEM;
> > > > > + goto unlock;
> > > > > + }
> > > > > +
> > > > > + otg->dev = dev;
> > > > > + otg->caps = config->otg_caps;
> > > > > +
> > > > > + if ((otg->caps->hnp_support || otg->caps->srp_support ||
> > > > > + otg->caps->adp_support) && !config->otg_work)
> > > > > + dev_info(dev, "otg: limiting to dual-role\n");
> > > >
> > > > dev_err, this should be an error.
> > >
> > > The condition may be wrong, but it is an information to show that
> > > current OTG is dual-role.
> >
> > This should not happen in any correct design, I even doubt if we
> > should try to continue by "downgrade" it to be duel role, currently
> > the only example user is dual role, so doing like this can't be tested
> > by real case, this downgrade is not so easy like we image, at least
> > for chipidea otg driver, simply replace a queue worker may not work,
> > as we have much more difference between the 2 configs.
> >
>
> Would you show more why chipidea can't work just replace the work item,
> and see if anything we still can improve for this framework?

In real OTG, we need enable AVV irq, but for duel role, nobody care/handle,
there are much more resource required for OTG: timers, hnp polling,
otg test device handling...

with current design, chipidea driver can support real OTG with its own
queue worker, or DRD with Roger's drd work item if config is correct.

But improve something to work on a *wrong* config will make it complicated
and does not make much sense IMO.

Li Jun
>
> --
> Best Regards,
> Peter Chen

2016-04-26 08:21:24

by Peter Chen

[permalink] [raw]
Subject: Re: [PATCH v6 07/12] usb: otg: add OTG/dual-role core

On Tue, Apr 26, 2016 at 07:00:22AM +0000, Jun Li wrote:
> Hi
>
> > -----Original Message-----
> > From: Peter Chen [mailto:[email protected]]
> > Sent: Tuesday, April 26, 2016 2:28 PM
> > To: Jun Li <[email protected]>
> > Cc: Roger Quadros <[email protected]>; [email protected];
> > [email protected]; [email protected]; [email protected];
> > [email protected]; [email protected];
> > [email protected]; [email protected]; [email protected];
> > [email protected]; [email protected]; [email protected];
> > [email protected]; [email protected]
> > Subject: Re: [PATCH v6 07/12] usb: otg: add OTG/dual-role core
> >
> > On Tue, Apr 26, 2016 at 05:11:36AM +0000, Jun Li wrote:
> > > Hi
> > >
> > > > -----Original Message-----
> > > > From: Peter Chen [mailto:[email protected]]
> > > > Sent: Tuesday, April 26, 2016 11:47 AM
> > > > To: Jun Li <[email protected]>
> > > > Cc: Roger Quadros <[email protected]>; [email protected];
> > > > [email protected]; [email protected];
> > > > [email protected]; [email protected];
> > > > [email protected]; [email protected];
> > > > [email protected]; [email protected]; [email protected];
> > > > [email protected]; [email protected];
> > > > [email protected]; [email protected]
> > > > Subject: Re: [PATCH v6 07/12] usb: otg: add OTG/dual-role core
> > > >
> > > > On Tue, Apr 26, 2016 at 02:07:56AM +0000, Jun Li wrote:
> > > > > > +struct usb_otg *usb_otg_register(struct device *dev,
> > > > > > + struct usb_otg_config *config) {
> > > > > > + struct usb_otg *otg;
> > > > > > + struct otg_wait_data *wait;
> > > > > > + int ret = 0;
> > > > > > +
> > > > > > + if (!dev || !config || !config->fsm_ops)
> > > > > > + return ERR_PTR(-EINVAL);
> > > > > > +
> > > > > > + /* already in list? */
> > > > > > + mutex_lock(&otg_list_mutex);
> > > > > > + if (usb_otg_get_data(dev)) {
> > > > > > + dev_err(dev, "otg: %s: device already in otg list\n",
> > > > > > + __func__);
> > > > > > + ret = -EINVAL;
> > > > > > + goto unlock;
> > > > > > + }
> > > > > > +
> > > > > > + /* allocate and add to list */
> > > > > > + otg = kzalloc(sizeof(*otg), GFP_KERNEL);
> > > > > > + if (!otg) {
> > > > > > + ret = -ENOMEM;
> > > > > > + goto unlock;
> > > > > > + }
> > > > > > +
> > > > > > + otg->dev = dev;
> > > > > > + otg->caps = config->otg_caps;
> > > > > > +
> > > > > > + if ((otg->caps->hnp_support || otg->caps->srp_support ||
> > > > > > + otg->caps->adp_support) && !config->otg_work)
> > > > > > + dev_info(dev, "otg: limiting to dual-role\n");
> > > > >
> > > > > dev_err, this should be an error.
> > > >
> > > > The condition may be wrong, but it is an information to show that
> > > > current OTG is dual-role.
> > >
> > > This should not happen in any correct design, I even doubt if we
> > > should try to continue by "downgrade" it to be duel role, currently
> > > the only example user is dual role, so doing like this can't be tested
> > > by real case, this downgrade is not so easy like we image, at least
> > > for chipidea otg driver, simply replace a queue worker may not work,
> > > as we have much more difference between the 2 configs.
> > >
> >
> > Would you show more why chipidea can't work just replace the work item,
> > and see if anything we still can improve for this framework?
>
> In real OTG, we need enable AVV irq,

Enable and Handling AVV is platform stuff. In this framework, we are
focus on how otg device manages host and gadget together, and the state
machine when the related otg event occurs.

> but for duel role, nobody care/handle,
> there are much more resource required for OTG: timers, hnp polling,
> otg test device handling...

They are common things for fully OTG fsm, you can move them
to common code (In fact, hnp polling handling is already common code).

>
> with current design, chipidea driver can support real OTG with its own
> queue worker, or DRD with Roger's drd work item if config is correct.
>
> But improve something to work on a *wrong* config will make it complicated
> and does not make much sense IMO.
>

What does above "config" you mean?

If the configure is fully OTG, you can choose different state machine,
eg otg_statemachine, if you find it is hard for chipidea to use this
framework, just list the reason, and see if we can improve.

--
Best Regards,
Peter Chen

2016-04-27 03:23:03

by Peter Chen

[permalink] [raw]
Subject: Re: [PATCH v6 07/12] usb: otg: add OTG/dual-role core

On Tue, Apr 26, 2016 at 04:21:07PM +0800, Peter Chen wrote:
> On Tue, Apr 26, 2016 at 07:00:22AM +0000, Jun Li wrote:
> > Hi
> >
> > > -----Original Message-----
> > > From: Peter Chen [mailto:[email protected]]
> > > Sent: Tuesday, April 26, 2016 2:28 PM
> > > To: Jun Li <[email protected]>
> > > Cc: Roger Quadros <[email protected]>; [email protected];
> > > [email protected]; [email protected]; [email protected];
> > > [email protected]; [email protected];
> > > [email protected]; [email protected]; [email protected];
> > > [email protected]; [email protected]; [email protected];
> > > [email protected]; [email protected]
> > > Subject: Re: [PATCH v6 07/12] usb: otg: add OTG/dual-role core
> > >
> > > On Tue, Apr 26, 2016 at 05:11:36AM +0000, Jun Li wrote:
> > > > Hi
> > > >
> > > > > -----Original Message-----
> > > > > From: Peter Chen [mailto:[email protected]]
> > > > > Sent: Tuesday, April 26, 2016 11:47 AM
> > > > > To: Jun Li <[email protected]>
> > > > > Cc: Roger Quadros <[email protected]>; [email protected];
> > > > > [email protected]; [email protected];
> > > > > [email protected]; [email protected];
> > > > > [email protected]; [email protected];
> > > > > [email protected]; [email protected]; [email protected];
> > > > > [email protected]; [email protected];
> > > > > [email protected]; [email protected]
> > > > > Subject: Re: [PATCH v6 07/12] usb: otg: add OTG/dual-role core
> > > > >
> > > > > On Tue, Apr 26, 2016 at 02:07:56AM +0000, Jun Li wrote:
> > > > > > > +struct usb_otg *usb_otg_register(struct device *dev,
> > > > > > > + struct usb_otg_config *config) {
> > > > > > > + struct usb_otg *otg;
> > > > > > > + struct otg_wait_data *wait;
> > > > > > > + int ret = 0;
> > > > > > > +
> > > > > > > + if (!dev || !config || !config->fsm_ops)
> > > > > > > + return ERR_PTR(-EINVAL);
> > > > > > > +
> > > > > > > + /* already in list? */
> > > > > > > + mutex_lock(&otg_list_mutex);
> > > > > > > + if (usb_otg_get_data(dev)) {
> > > > > > > + dev_err(dev, "otg: %s: device already in otg list\n",
> > > > > > > + __func__);
> > > > > > > + ret = -EINVAL;
> > > > > > > + goto unlock;
> > > > > > > + }
> > > > > > > +
> > > > > > > + /* allocate and add to list */
> > > > > > > + otg = kzalloc(sizeof(*otg), GFP_KERNEL);
> > > > > > > + if (!otg) {
> > > > > > > + ret = -ENOMEM;
> > > > > > > + goto unlock;
> > > > > > > + }
> > > > > > > +
> > > > > > > + otg->dev = dev;
> > > > > > > + otg->caps = config->otg_caps;
> > > > > > > +
> > > > > > > + if ((otg->caps->hnp_support || otg->caps->srp_support ||
> > > > > > > + otg->caps->adp_support) && !config->otg_work)
> > > > > > > + dev_info(dev, "otg: limiting to dual-role\n");
> > > > > >
> > > > > > dev_err, this should be an error.
> > > > >
> > > > > The condition may be wrong, but it is an information to show that
> > > > > current OTG is dual-role.
> > > >
> > > > This should not happen in any correct design, I even doubt if we
> > > > should try to continue by "downgrade" it to be duel role, currently
> > > > the only example user is dual role, so doing like this can't be tested
> > > > by real case, this downgrade is not so easy like we image, at least
> > > > for chipidea otg driver, simply replace a queue worker may not work,
> > > > as we have much more difference between the 2 configs.
> > > >
> > >
> > > Would you show more why chipidea can't work just replace the work item,
> > > and see if anything we still can improve for this framework?
> >
> > In real OTG, we need enable AVV irq,
>
> Enable and Handling AVV is platform stuff. In this framework, we are
> focus on how otg device manages host and gadget together, and the state
> machine when the related otg event occurs.
>
> > but for duel role, nobody care/handle,
> > there are much more resource required for OTG: timers, hnp polling,
> > otg test device handling...
>
> They are common things for fully OTG fsm, you can move them
> to common code (In fact, hnp polling handling is already common code).
>
> >
> > with current design, chipidea driver can support real OTG with its own
> > queue worker, or DRD with Roger's drd work item if config is correct.
> >
> > But improve something to work on a *wrong* config will make it complicated
> > and does not make much sense IMO.
> >
>
> What does above "config" you mean?
>
> If the configure is fully OTG, you can choose different state machine,
> eg otg_statemachine, if you find it is hard for chipidea to use this
> framework, just list the reason, and see if we can improve.
>

Roger, after discussing with Jun off line, we think usb_otg_register
should return -ENOTSUPP if platform is OTG capabilities (HNP || SRP ||
ADP), since this patch set does not cover fully otg features, the users
should not be confused when try to implement fully otg using this
framework.

Later, after your patch set is merged, we can add fully OTG features
using this framework, and remove this check.

What's your opinion?

--

Best Regards,
Peter Chen

2016-04-27 10:16:40

by Jun Li

[permalink] [raw]
Subject: RE: [PATCH v6 08/12] usb: hcd: Adapt to OTG core

Hi

>
> +
> +static struct otg_hcd_ops otg_hcd_intf = {
> + .add = usb_add_hcd,
> + .remove = usb_remove_hcd,
> + .usb_bus_start_enum = usb_bus_start_enum,

Build break if CONFIG_USB_OTG is not enabled:

drivers/built-in.o:(.data+0x1db30): undefined reference to `usb_bus_start_enum'
Makefile:948: recipe for target 'vmlinux' failed
make: *** [vmlinux] Error 1

Li Jun

> + .usb_control_msg = usb_control_msg,
> + .usb_hub_find_child = usb_hub_find_child, };
> +

2016-04-27 11:00:16

by Roger Quadros

[permalink] [raw]
Subject: Re: [PATCH v6 07/12] usb: otg: add OTG/dual-role core

Hi,

On 27/04/16 06:15, Peter Chen wrote:
> On Tue, Apr 26, 2016 at 04:21:07PM +0800, Peter Chen wrote:
>> On Tue, Apr 26, 2016 at 07:00:22AM +0000, Jun Li wrote:
>>> Hi
>>>
>>>> -----Original Message-----
>>>> From: Peter Chen [mailto:[email protected]]
>>>> Sent: Tuesday, April 26, 2016 2:28 PM
>>>> To: Jun Li <[email protected]>
>>>> Cc: Roger Quadros <[email protected]>; [email protected];
>>>> [email protected]; [email protected]; [email protected];
>>>> [email protected]; [email protected];
>>>> [email protected]; [email protected]; [email protected];
>>>> [email protected]; [email protected]; [email protected];
>>>> [email protected]; [email protected]
>>>> Subject: Re: [PATCH v6 07/12] usb: otg: add OTG/dual-role core
>>>>
>>>> On Tue, Apr 26, 2016 at 05:11:36AM +0000, Jun Li wrote:
>>>>> Hi
>>>>>
>>>>>> -----Original Message-----
>>>>>> From: Peter Chen [mailto:[email protected]]
>>>>>> Sent: Tuesday, April 26, 2016 11:47 AM
>>>>>> To: Jun Li <[email protected]>
>>>>>> Cc: Roger Quadros <[email protected]>; [email protected];
>>>>>> [email protected]; [email protected];
>>>>>> [email protected]; [email protected];
>>>>>> [email protected]; [email protected];
>>>>>> [email protected]; [email protected]; [email protected];
>>>>>> [email protected]; [email protected];
>>>>>> [email protected]; [email protected]
>>>>>> Subject: Re: [PATCH v6 07/12] usb: otg: add OTG/dual-role core
>>>>>>
>>>>>> On Tue, Apr 26, 2016 at 02:07:56AM +0000, Jun Li wrote:
>>>>>>>> +struct usb_otg *usb_otg_register(struct device *dev,
>>>>>>>> + struct usb_otg_config *config) {
>>>>>>>> + struct usb_otg *otg;
>>>>>>>> + struct otg_wait_data *wait;
>>>>>>>> + int ret = 0;
>>>>>>>> +
>>>>>>>> + if (!dev || !config || !config->fsm_ops)
>>>>>>>> + return ERR_PTR(-EINVAL);
>>>>>>>> +
>>>>>>>> + /* already in list? */
>>>>>>>> + mutex_lock(&otg_list_mutex);
>>>>>>>> + if (usb_otg_get_data(dev)) {
>>>>>>>> + dev_err(dev, "otg: %s: device already in otg list\n",
>>>>>>>> + __func__);
>>>>>>>> + ret = -EINVAL;
>>>>>>>> + goto unlock;
>>>>>>>> + }
>>>>>>>> +
>>>>>>>> + /* allocate and add to list */
>>>>>>>> + otg = kzalloc(sizeof(*otg), GFP_KERNEL);
>>>>>>>> + if (!otg) {
>>>>>>>> + ret = -ENOMEM;
>>>>>>>> + goto unlock;
>>>>>>>> + }
>>>>>>>> +
>>>>>>>> + otg->dev = dev;
>>>>>>>> + otg->caps = config->otg_caps;
>>>>>>>> +
>>>>>>>> + if ((otg->caps->hnp_support || otg->caps->srp_support ||
>>>>>>>> + otg->caps->adp_support) && !config->otg_work)
>>>>>>>> + dev_info(dev, "otg: limiting to dual-role\n");
>>>>>>>
>>>>>>> dev_err, this should be an error.
>>>>>>
>>>>>> The condition may be wrong, but it is an information to show that
>>>>>> current OTG is dual-role.
>>>>>
>>>>> This should not happen in any correct design, I even doubt if we
>>>>> should try to continue by "downgrade" it to be duel role, currently
>>>>> the only example user is dual role, so doing like this can't be tested
>>>>> by real case, this downgrade is not so easy like we image, at least
>>>>> for chipidea otg driver, simply replace a queue worker may not work,
>>>>> as we have much more difference between the 2 configs.
>>>>>
>>>>
>>>> Would you show more why chipidea can't work just replace the work item,
>>>> and see if anything we still can improve for this framework?
>>>
>>> In real OTG, we need enable AVV irq,
>>
>> Enable and Handling AVV is platform stuff. In this framework, we are
>> focus on how otg device manages host and gadget together, and the state
>> machine when the related otg event occurs.
>>
>>> but for duel role, nobody care/handle,
>>> there are much more resource required for OTG: timers, hnp polling,
>>> otg test device handling...
>>
>> They are common things for fully OTG fsm, you can move them
>> to common code (In fact, hnp polling handling is already common code).
>>
>>>
>>> with current design, chipidea driver can support real OTG with its own
>>> queue worker, or DRD with Roger's drd work item if config is correct.
>>>
>>> But improve something to work on a *wrong* config will make it complicated
>>> and does not make much sense IMO.
>>>
>>
>> What does above "config" you mean?
>>
>> If the configure is fully OTG, you can choose different state machine,
>> eg otg_statemachine, if you find it is hard for chipidea to use this
>> framework, just list the reason, and see if we can improve.
>>
>
> Roger, after discussing with Jun off line, we think usb_otg_register
> should return -ENOTSUPP if platform is OTG capabilities (HNP || SRP ||
> ADP), since this patch set does not cover fully otg features, the users

But this series isn't preventing full otg implementation. You can
still do that via config->otg_work.

I can modify the following condition to return -ENOTSUPP instead of
defaulting to dual-role

struct usb_otg *usb_otg_register(...)
{
...
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");
return -ENOTSUPP;
}
...
}

Is this sufficient?

> should not be confused when try to implement fully otg using this
> framework.
>
> Later, after your patch set is merged, we can add fully OTG features
> using this framework, and remove this check.
>
> What's your opinion?
>

--
cheers,
-roger

2016-04-27 11:01:12

by Roger Quadros

[permalink] [raw]
Subject: Re: [PATCH v6 08/12] usb: hcd: Adapt to OTG core

On 27/04/16 13:16, Jun Li wrote:
> Hi
>
>>
>> +
>> +static struct otg_hcd_ops otg_hcd_intf = {
>> + .add = usb_add_hcd,
>> + .remove = usb_remove_hcd,
>> + .usb_bus_start_enum = usb_bus_start_enum,
>
> Build break if CONFIG_USB_OTG is not enabled:
>
> drivers/built-in.o:(.data+0x1db30): undefined reference to `usb_bus_start_enum'
> Makefile:948: recipe for target 'vmlinux' failed
> make: *** [vmlinux] Error 1

Thanks. Will fix it.

cheers,
-roger

>
>> + .usb_control_msg = usb_control_msg,
>> + .usb_hub_find_child = usb_hub_find_child, };
>> +
>

2016-04-27 11:12:16

by Roger Quadros

[permalink] [raw]
Subject: Re: [PATCH v6 08/12] usb: hcd: Adapt to OTG core

On 27/04/16 14:00, Roger Quadros wrote:
> On 27/04/16 13:16, Jun Li wrote:
>> Hi
>>
>>>
>>> +
>>> +static struct otg_hcd_ops otg_hcd_intf = {
>>> + .add = usb_add_hcd,
>>> + .remove = usb_remove_hcd,
>>> + .usb_bus_start_enum = usb_bus_start_enum,
>>
>> Build break if CONFIG_USB_OTG is not enabled:
>>
>> drivers/built-in.o:(.data+0x1db30): undefined reference to `usb_bus_start_enum'
>> Makefile:948: recipe for target 'vmlinux' failed
>> make: *** [vmlinux] Error 1

I couldn't get this error. Could you please send me your .config? Thanks.

cheers,
-roger
>
> Thanks. Will fix it.
>
> cheers,
> -roger
>
>>
>>> + .usb_control_msg = usb_control_msg,
>>> + .usb_hub_find_child = usb_hub_find_child, };
>>> +
>>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-usb" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html
>

2016-04-27 11:16:15

by Roger Quadros

[permalink] [raw]
Subject: Re: [PATCH v6 07/12] usb: otg: add OTG/dual-role core

Hi Jun,

On 26/04/16 05:07, Jun Li wrote:
> Hi Roger
>
>> -----Original Message-----
>> From: Roger Quadros [mailto:[email protected]]
>> Sent: Tuesday, April 05, 2016 10:05 PM
>> To: [email protected]; [email protected];
>> [email protected]; [email protected]
>> Cc: [email protected]; [email protected];
>> [email protected]; [email protected]; [email protected];
>> [email protected]; [email protected]; [email protected];
>> [email protected]; [email protected]; Roger Quadros
>> <[email protected]>
>> Subject: [PATCH v6 07/12] usb: otg: add OTG/dual-role core
>>
>> 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 <[email protected]>
>> ---
>
> ...
>
>> +/**
>> + * Register pending host/gadget and remove entry from wait list */
>> +static void usb_otg_flush_wait(struct device *otg_dev) {
>> + struct otg_wait_data *wait;
>> + struct otg_hcd *host;
>> + struct otg_gcd *gadget;
>> +
>> + mutex_lock(&wait_list_mutex);
>> +
>> + wait = usb_otg_get_wait(otg_dev);
>> + if (!wait)
>> + goto done;
>> +
>> + dev_dbg(otg_dev, "otg: registering pending host/gadget\n");
>> + gadget = &wait->gcd;
>> + if (gadget)
>
> If (gadget->gadget)

good catch :)
I'll probably rename the local variables
host to hcd
gadget to gcd.

>
>> + usb_otg_register_gadget(gadget->gadget, gadget->ops);
>> +
>> + host = &wait->primary_hcd;
>> + if (host->hcd)
>> + usb_otg_register_hcd(host->hcd, host->irqnum, host->irqflags,
>> + host->ops);
>> +
>> + host = &wait->shared_hcd;
>> + if (host->hcd)
>> + usb_otg_register_hcd(host->hcd, host->irqnum, host->irqflags,
>> + host->ops);
>> +
>> + list_del(&wait->list);
>> + kfree(wait);
>> +
>> +done:
>> + mutex_unlock(&wait_list_mutex);
>> +}
>> +
>> +/**
>> + * Check if the OTG device is in our OTG list and return
>> + * usb_otg data, else NULL.
>> + *
>> + * otg_list_mutex must be held.
>> + */
>> +static struct usb_otg *usb_otg_get_data(struct device *otg_dev) {
>> + struct usb_otg *otg;
>> +
>> + if (!otg_dev)
>> + return NULL;
>> +
>> + list_for_each_entry(otg, &otg_list, list) {
>> + if (otg->dev == otg_dev)
>> + return otg;
>> + }
>> +
>> + return NULL;
>> +}
>
> Could you export it to be a public API, we may need access usb_otg
> in common host driver for handling of enumeration of otg test device.

We can always do that later. As of now nobody is using it so let's keep it private.
>
> ...
>
>> +/**
>> + * Called when entering a DRD state.
>> + * fsm->lock must be held.
>> + */
>> +static void drd_set_state(struct otg_fsm *fsm, enum usb_otg_state
>> +new_state) {
>> + struct usb_otg *otg = container_of(fsm, struct usb_otg, fsm);
>> +
>> + if (otg->state == new_state)
>> + return;
>> +
>> + fsm->state_changed = 1;
>> + dev_dbg(otg->dev, "otg: set state: %s\n",
>> + usb_otg_state_string(new_state));
>> + switch (new_state) {
>> + case OTG_STATE_B_IDLE:
>> + drd_set_protocol(fsm, PROTO_UNDEF);
>> + otg_drv_vbus(otg, 0);
>> + break;
>> + case OTG_STATE_B_PERIPHERAL:
>> + drd_set_protocol(fsm, PROTO_GADGET);
>> + otg_drv_vbus(otg, 0);
>> + break;
>> + case OTG_STATE_A_HOST:
>> + drd_set_protocol(fsm, PROTO_HOST);
>> + otg_drv_vbus(otg, 1);
>> + break;
>> + 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:
>
> Remove above unused states.

OK.
>
>> + default:
>> + dev_warn(otg->dev, "%s: otg: invalid state: %s\n",
>> + __func__, usb_otg_state_string(new_state));
>> + break;
>> + }
>> +
>> + otg->state = new_state;
>> +}
>> +
>> +/**
>> + * DRD state change judgement
>> + *
>> + * For DRD we're only interested in some of the OTG states
>> + * i.e. OTG_STATE_B_IDLE: both peripheral and host are stopped
>> + * OTG_STATE_B_PERIPHERAL: peripheral active
>> + * OTG_STATE_A_HOST: host active
>> + * we're only interested in the following inputs
>> + * fsm->id, fsm->b_sess_vld
>> + */
>> +int drd_statemachine(struct usb_otg *otg) {
>> + struct otg_fsm *fsm = &otg->fsm;
>> + enum usb_otg_state state;
>> + int ret;
>> +
>> + mutex_lock(&fsm->lock);
>> +
>> + fsm->state_changed = 0;
>> + state = otg->state;
>> +
>> + switch (state) {
>> + case OTG_STATE_UNDEFINED:
>> + if (!fsm->id)
>> + drd_set_state(fsm, OTG_STATE_A_HOST);
>> + else if (fsm->id && fsm->b_sess_vld)
>> + drd_set_state(fsm, OTG_STATE_B_PERIPHERAL);
>> + else
>> + drd_set_state(fsm, OTG_STATE_B_IDLE);
>> + break;
>> + case OTG_STATE_B_IDLE:
>> + if (!fsm->id)
>> + drd_set_state(fsm, OTG_STATE_A_HOST);
>> + else if (fsm->b_sess_vld)
>> + drd_set_state(fsm, OTG_STATE_B_PERIPHERAL);
>> + break;
>> + case OTG_STATE_B_PERIPHERAL:
>> + if (!fsm->id)
>> + drd_set_state(fsm, OTG_STATE_A_HOST);
>> + else if (!fsm->b_sess_vld)
>> + drd_set_state(fsm, OTG_STATE_B_IDLE);
>> + break;
>> + case OTG_STATE_A_HOST:
>> + if (fsm->id && fsm->b_sess_vld)
>> + drd_set_state(fsm, OTG_STATE_B_PERIPHERAL);
>> + else if (fsm->id && !fsm->b_sess_vld)
>> + drd_set_state(fsm, OTG_STATE_B_IDLE);
>> + break;
>> +
>> + /* 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:
>
> Remove above unused states and add a default:

OK.
>
>> + dev_err(otg->dev, "%s: otg: invalid usb-drd state: %s\n",
>> + __func__, usb_otg_state_string(state));
>> + drd_set_state(fsm, OTG_STATE_UNDEFINED);
>> + 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);
>> +
>> +/**
>> + * OTG FSM/DRD work function
>
> DRD work function

Yes.
>
>> + */
>> +static void usb_otg_work(struct work_struct *work) {
>
> usb_drd_work() name is better as it's only for drd.

Agreed.
>
>> + struct usb_otg *otg = container_of(work, struct usb_otg, work);
>> +
>> + pm_runtime_get_sync(otg->dev);
>> + drd_statemachine(otg);
>> + pm_runtime_put_sync(otg->dev);
>> +}
>> +
>> +/**
>> + * usb_otg_register() - Register the OTG/dual-role device to OTG core
>> + * @dev: OTG/dual-role controller device.
>> + * @config: OTG configuration.
>> + *
>> + * Registers the OTG/dual-role controller device with the USB OTG core.
>> + *
>> + * Return: struct usb_otg * if success, ERR_PTR() if error.
>> + */
>> +struct usb_otg *usb_otg_register(struct device *dev,
>> + struct usb_otg_config *config)
>> +{
>> + struct usb_otg *otg;
>> + struct otg_wait_data *wait;
>> + int ret = 0;
>> +
>> + if (!dev || !config || !config->fsm_ops)
>> + return ERR_PTR(-EINVAL);
>> +
>> + /* already in list? */
>> + mutex_lock(&otg_list_mutex);
>> + if (usb_otg_get_data(dev)) {
>> + dev_err(dev, "otg: %s: device already in otg list\n",
>> + __func__);
>> + ret = -EINVAL;
>> + goto unlock;
>> + }
>> +
>> + /* allocate and add to list */
>> + otg = kzalloc(sizeof(*otg), GFP_KERNEL);
>> + if (!otg) {
>> + ret = -ENOMEM;
>> + goto unlock;
>> + }
>> +
>> + otg->dev = dev;
>> + otg->caps = config->otg_caps;
>> +
>> + if ((otg->caps->hnp_support || otg->caps->srp_support ||
>> + otg->caps->adp_support) && !config->otg_work)
>> + dev_info(dev, "otg: limiting to dual-role\n");
>
> dev_err, this should be an error.

Yes, I'll update it to like so.

dev_err(dev, "otg: otg_work function must be provided for OTG\n");
return -EINVAL;

cheers,
-roger
>
>> +
>> + if (config->otg_work) /* custom otg_work ? */
>> + INIT_WORK(&otg->work, config->otg_work);
>> + else
>> + INIT_WORK(&otg->work, usb_otg_work);
>> +
>> + otg->wq = create_singlethread_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);
>> +
>> + /* were we in wait list? */
>> + mutex_lock(&wait_list_mutex);
>> + wait = usb_otg_get_wait(dev);
>> + mutex_unlock(&wait_list_mutex);
>> + if (wait) {
>> + /* register pending host/gadget and flush from list */
>> + usb_otg_flush_wait(dev);
>> + }
>> +
>> + return otg;
>> +
>> +err_wq:
>> + kfree(otg);
>> +unlock:
>> + mutex_unlock(&otg_list_mutex);
>> + return ERR_PTR(ret);
>> +}
>> +EXPORT_SYMBOL_GPL(usb_otg_register);
>> +
>

2016-04-27 11:23:12

by Roger Quadros

[permalink] [raw]
Subject: Re: [PATCH v6 09/12] usb: gadget: udc: adapt to OTG core

On 26/04/16 03:07, Jun Li wrote:
> Hi
>
>> -----Original Message-----
>> From: Roger Quadros [mailto:[email protected]]
>> Sent: Monday, April 25, 2016 10:04 PM
>> To: Jun Li <[email protected]>; [email protected]; [email protected];
>> [email protected]; [email protected]
>> Cc: [email protected]; [email protected];
>> [email protected]; [email protected]; [email protected];
>> [email protected]; [email protected]; [email protected];
>> [email protected]; [email protected]
>> Subject: Re: [PATCH v6 09/12] usb: gadget: udc: adapt to OTG core
>>
>> Hi,
>>
>> On 21/04/16 09:38, Jun Li wrote:
>>> Hi,
>>>
>>> ...
>>>>
>>>> /**
>>>> + * usb_gadget_start - start the usb gadget controller and connect to
>>>> +bus
>>>> + * @gadget: the gadget device to start
>>>> + *
>>>> + * This is external API for use by OTG core.
>>>> + *
>>>> + * Start the usb device controller and connect to bus (enable pull).
>>>> + */
>>>> +static int usb_gadget_start(struct usb_gadget *gadget) {
>>>> + int ret;
>>>> + struct usb_udc *udc = NULL;
>>>> +
>>>> + dev_dbg(&gadget->dev, "%s\n", __func__);
>>>> + mutex_lock(&udc_lock);
>>>> + list_for_each_entry(udc, &udc_list, list)
>>>> + if (udc->gadget == gadget)
>>>> + goto found;
>>>> +
>>>> + dev_err(gadget->dev.parent, "%s: gadget not registered.\n",
>>>> + __func__);
>>>> + mutex_unlock(&udc_lock);
>>>> + return -EINVAL;
>>>> +
>>>> +found:
>>>> + ret = usb_gadget_udc_start(udc);
>>>> + if (ret)
>>>> + dev_err(&udc->dev, "USB Device Controller didn't start: %d\n",
>>>> + ret);
>>>> + else
>>>> + usb_udc_connect_control(udc);
>>>
>>> For drd, it's fine, but for real otg, gadget connect should be done by
>>> loc_conn() instead of gadget start.
>>
>> It is upto the OTG state machine to call gadget_start() when it needs to
>> connect to the bus (i.e. loc_conn()). I see no point in calling gadget
>> start before.
>>
>> Do you see any issue in doing so?
>
> This is what OTG state machine does:
> case OTG_STATE_B_PERIPHERAL:
> otg_chrg_vbus(otg, 0);
> otg_loc_sof(otg, 0);
> otg_set_protocol(fsm, PROTO_GADGET);
> otg_loc_conn(otg, 1);
> break;
>
> You intend to abstract something common in this api when start gadget,
> which should be called by otg_set_protocol(fsm, PROTO_GADGET); and
> drd_set_protocol(fsm, PROTO_GADGET); right?
>
> So you may move usb_udc_connect_control(IMO usb_gadget_connect()
> is better)out of usb_gadget_start(), then for drd:
>
> case OTG_STATE_B_PERIPHERAL:
> drd_set_protocol(fsm, PROTO_GADGET);
> otg_drv_vbus(otg, 0);
> usb_gadget_connect();

OK. I understand now. I'll implement your suggestion. Thanks.

cheers,
-roger

>>>
>>>> +
>>>> + mutex_unlock(&udc_lock);
>>>> +
>>>> + return ret;
>>>> +}
>>>> +
>>>> +/**
>>>> + * usb_gadget_stop - disconnect from bus and stop the usb gadget
>>>> + * @gadget: The gadget device we want to stop
>>>> + *
>>>> + * This is external API for use by OTG core.
>>>> + *
>>>> + * Disconnect from the bus (disable pull) and stop the
>>>> + * gadget controller.
>>>> + */
>>>> +static int usb_gadget_stop(struct usb_gadget *gadget) {
>>>> + struct usb_udc *udc = NULL;
>>>> +
>>>> + dev_dbg(&gadget->dev, "%s\n", __func__);
>>>> + mutex_lock(&udc_lock);
>>>> + list_for_each_entry(udc, &udc_list, list)
>>>> + if (udc->gadget == gadget)
>>>> + goto found;
>>>> +
>>>> + dev_err(gadget->dev.parent, "%s: gadget not registered.\n",
>>>> + __func__);
>>>> + mutex_unlock(&udc_lock);
>>>> + return -EINVAL;
>>>> +
>>>> +found:
>>>> + usb_gadget_disconnect(udc->gadget);
>>>
>>> Likewise, gadget disconnect also should be done by loc_conn() instead
>>> of gadget stop.
>>>
>>>> + udc->driver->disconnect(udc->gadget);
>>>> + usb_gadget_udc_stop(udc);
>>>> + mutex_unlock(&udc_lock);
>>>> +
>>>> + return 0;
>>>> +}
>>>> +
>>>
>>> Li Jun
>>>

2016-04-27 12:50:01

by Jun Li

[permalink] [raw]
Subject: RE: [PATCH v6 08/12] usb: hcd: Adapt to OTG core

Hi
> -----Original Message-----
> From: Roger Quadros [mailto:[email protected]]
> Sent: Wednesday, April 27, 2016 7:12 PM
> To: Jun Li <[email protected]>; [email protected]; [email protected];
> [email protected]; [email protected]
> Cc: [email protected]; [email protected];
> [email protected]; [email protected]; [email protected];
> [email protected]; [email protected]; [email protected];
> [email protected]; [email protected]
> Subject: Re: [PATCH v6 08/12] usb: hcd: Adapt to OTG core
>
> On 27/04/16 14:00, Roger Quadros wrote:
> > On 27/04/16 13:16, Jun Li wrote:
> >> Hi
> >>
> >>>
> >>> +
> >>> +static struct otg_hcd_ops otg_hcd_intf = {
> >>> + .add = usb_add_hcd,
> >>> + .remove = usb_remove_hcd,
> >>> + .usb_bus_start_enum = usb_bus_start_enum,
> >>
> >> Build break if CONFIG_USB_OTG is not enabled:
> >>
> >> drivers/built-in.o:(.data+0x1db30): undefined reference to
> `usb_bus_start_enum'
> >> Makefile:948: recipe for target 'vmlinux' failed
> >> make: *** [vmlinux] Error 1
>
> I couldn't get this error. Could you please send me your .config? Thanks.

imx_v6_v7_defconfig

>
> cheers,
> -roger
> >
> > Thanks. Will fix it.
> >
> > cheers,
> > -roger
> >
> >>
> >>> + .usb_control_msg = usb_control_msg,
> >>> + .usb_hub_find_child = usb_hub_find_child, };
> >>> +
> >>
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-usb"
> > in the body of a message to [email protected] More majordomo
> > info at http://vger.kernel.org/majordomo-info.html
> >

2016-04-27 13:33:26

by Jun Li

[permalink] [raw]
Subject: RE: [PATCH v6 08/12] usb: hcd: Adapt to OTG core

Hi,

> -----Original Message-----
> From: Jun Li [mailto:[email protected]]
> Sent: Wednesday, April 27, 2016 8:50 PM
> To: Roger Quadros <[email protected]>; [email protected];
> [email protected]; [email protected]; [email protected]
> Cc: [email protected]; [email protected];
> [email protected]; [email protected]; [email protected];
> [email protected]; [email protected]; [email protected];
> [email protected]; [email protected]
> Subject: RE: [PATCH v6 08/12] usb: hcd: Adapt to OTG core
>
> Hi
> > -----Original Message-----
> > From: Roger Quadros [mailto:[email protected]]
> > Sent: Wednesday, April 27, 2016 7:12 PM
> > To: Jun Li <[email protected]>; [email protected];
> > [email protected]; [email protected]; [email protected]
> > Cc: [email protected]; [email protected];
> > [email protected]; [email protected];
> > [email protected]; [email protected]; [email protected];
> > [email protected]; [email protected];
> > [email protected]
> > Subject: Re: [PATCH v6 08/12] usb: hcd: Adapt to OTG core
> >
> > On 27/04/16 14:00, Roger Quadros wrote:
> > > On 27/04/16 13:16, Jun Li wrote:
> > >> Hi
> > >>
> > >>>
> > >>> +
> > >>> +static struct otg_hcd_ops otg_hcd_intf = {
> > >>> + .add = usb_add_hcd,
> > >>> + .remove = usb_remove_hcd,
> > >>> + .usb_bus_start_enum = usb_bus_start_enum,
> > >>
> > >> Build break if CONFIG_USB_OTG is not enabled:
> > >>
> > >> drivers/built-in.o:(.data+0x1db30): undefined reference to
> > `usb_bus_start_enum'
> > >> Makefile:948: recipe for target 'vmlinux' failed
> > >> make: *** [vmlinux] Error 1
> >
> > I couldn't get this error. Could you please send me your .config? Thanks.
>
> imx_v6_v7_defconfig

My bad, no problem now.

Li Jun

>
> >
> > cheers,
> > -roger
> > >
> > > Thanks. Will fix it.
> > >
> > > cheers,
> > > -roger
> > >
> > >>
> > >>> + .usb_control_msg = usb_control_msg,
> > >>> + .usb_hub_find_child = usb_hub_find_child, };
> > >>> +
> > >>
> > > --
> > > To unsubscribe from this list: send the line "unsubscribe linux-usb"
> > > in the body of a message to [email protected] More majordomo
> > > info at http://vger.kernel.org/majordomo-info.html
> > >

2016-04-27 14:35:43

by Roger Quadros

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

On 18/04/16 10:08, Peter Chen wrote:
> On Tue, Apr 05, 2016 at 05:05:16PM +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.
>
> Since we have still not added fsm.a_set_b_hnp_en in OTG FSM, and this
> patch set is mainly for DRD, would you please move out this patch from
> this set?

Sure.

cheers,
-roger

>
>>
>> Signed-off-by: Roger Quadros <[email protected]>
>> ---
>> drivers/usb/core/hub.c | 17 +++++++++++++++--
>> 1 file changed, 15 insertions(+), 2 deletions(-)
>>
>> diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
>> index 38cc4ba..27e3b4c 100644
>> --- a/drivers/usb/core/hub.c
>> +++ b/drivers/usb/core/hub.c
>> @@ -2273,6 +2273,7 @@ static int usb_enumerate_device_otg(struct usb_device *udev)
>> && udev->parent == udev->bus->root_hub) {
>> struct usb_otg_descriptor *desc = NULL;
>> struct usb_bus *bus = udev->bus;
>> + struct usb_hcd *hcd = bus_to_hcd(bus);
>> unsigned port1 = udev->portnum;
>>
>> /* descriptor may appear anywhere in config */
>> @@ -2302,6 +2303,9 @@ static int usb_enumerate_device_otg(struct usb_device *udev)
>> dev_err(&udev->dev, "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(hcd->otg_dev);
>> }
>> } else if (desc->bLength == sizeof
>> (struct usb_otg_descriptor)) {
>> @@ -2312,10 +2316,14 @@ static int usb_enumerate_device_otg(struct usb_device *udev)
>> USB_DEVICE_A_ALT_HNP_SUPPORT,
>> 0, NULL, 0,
>> USB_CTRL_SET_TIMEOUT);
>> - if (err < 0)
>> + if (err < 0) {
>> dev_err(&udev->dev,
>> "set a_alt_hnp_support failed: %d\n",
>> err);
>> + } else {
>> + /* notify OTG fsm about a_set_b_hnp_enable */
>> + usb_otg_kick_fsm(hcd->otg_dev);
>> + }
>> }
>> }
>> #endif
>> @@ -4355,8 +4363,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(hcd->otg_dev);
>> +#endif
>> + }
>> }
>>
>> /* Some low speed devices have problems with the quick delay, so */
>> --
>> 2.5.0
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-usb" in
>> the body of a message to [email protected]
>> More majordomo info at http://vger.kernel.org/majordomo-info.html
>

2016-04-28 02:02:06

by Peter Chen

[permalink] [raw]
Subject: Re: [PATCH v6 07/12] usb: otg: add OTG/dual-role core

On Wed, Apr 27, 2016 at 01:59:44PM +0300, Roger Quadros wrote:
> Hi,
>
> On 27/04/16 06:15, Peter Chen wrote:
> > On Tue, Apr 26, 2016 at 04:21:07PM +0800, Peter Chen wrote:
> >> On Tue, Apr 26, 2016 at 07:00:22AM +0000, Jun Li wrote:
> >>> Hi
> >>>
> >>>> -----Original Message-----
> >>>> From: Peter Chen [mailto:[email protected]]
> >>>> Sent: Tuesday, April 26, 2016 2:28 PM
> >>>> To: Jun Li <[email protected]>
> >>>> Cc: Roger Quadros <[email protected]>; [email protected];
> >>>> [email protected]; [email protected]; [email protected];
> >>>> [email protected]; [email protected];
> >>>> [email protected]; [email protected]; [email protected];
> >>>> [email protected]; [email protected]; [email protected];
> >>>> [email protected]; [email protected]
> >>>> Subject: Re: [PATCH v6 07/12] usb: otg: add OTG/dual-role core
> >>>>
> >>>> On Tue, Apr 26, 2016 at 05:11:36AM +0000, Jun Li wrote:
> >>>>> Hi
> >>>>>
> >>>>>> -----Original Message-----
> >>>>>> From: Peter Chen [mailto:[email protected]]
> >>>>>> Sent: Tuesday, April 26, 2016 11:47 AM
> >>>>>> To: Jun Li <[email protected]>
> >>>>>> Cc: Roger Quadros <[email protected]>; [email protected];
> >>>>>> [email protected]; [email protected];
> >>>>>> [email protected]; [email protected];
> >>>>>> [email protected]; [email protected];
> >>>>>> [email protected]; [email protected]; [email protected];
> >>>>>> [email protected]; [email protected];
> >>>>>> [email protected]; [email protected]
> >>>>>> Subject: Re: [PATCH v6 07/12] usb: otg: add OTG/dual-role core
> >>>>>>
> >>>>>> On Tue, Apr 26, 2016 at 02:07:56AM +0000, Jun Li wrote:
> >>>>>>>> +struct usb_otg *usb_otg_register(struct device *dev,
> >>>>>>>> + struct usb_otg_config *config) {
> >>>>>>>> + struct usb_otg *otg;
> >>>>>>>> + struct otg_wait_data *wait;
> >>>>>>>> + int ret = 0;
> >>>>>>>> +
> >>>>>>>> + if (!dev || !config || !config->fsm_ops)
> >>>>>>>> + return ERR_PTR(-EINVAL);
> >>>>>>>> +
> >>>>>>>> + /* already in list? */
> >>>>>>>> + mutex_lock(&otg_list_mutex);
> >>>>>>>> + if (usb_otg_get_data(dev)) {
> >>>>>>>> + dev_err(dev, "otg: %s: device already in otg list\n",
> >>>>>>>> + __func__);
> >>>>>>>> + ret = -EINVAL;
> >>>>>>>> + goto unlock;
> >>>>>>>> + }
> >>>>>>>> +
> >>>>>>>> + /* allocate and add to list */
> >>>>>>>> + otg = kzalloc(sizeof(*otg), GFP_KERNEL);
> >>>>>>>> + if (!otg) {
> >>>>>>>> + ret = -ENOMEM;
> >>>>>>>> + goto unlock;
> >>>>>>>> + }
> >>>>>>>> +
> >>>>>>>> + otg->dev = dev;
> >>>>>>>> + otg->caps = config->otg_caps;
> >>>>>>>> +
> >>>>>>>> + if ((otg->caps->hnp_support || otg->caps->srp_support ||
> >>>>>>>> + otg->caps->adp_support) && !config->otg_work)
> >>>>>>>> + dev_info(dev, "otg: limiting to dual-role\n");
> >>>>>>>
> >>>>>>> dev_err, this should be an error.
> >>>>>>
> >>>>>> The condition may be wrong, but it is an information to show that
> >>>>>> current OTG is dual-role.
> >>>>>
> >>>>> This should not happen in any correct design, I even doubt if we
> >>>>> should try to continue by "downgrade" it to be duel role, currently
> >>>>> the only example user is dual role, so doing like this can't be tested
> >>>>> by real case, this downgrade is not so easy like we image, at least
> >>>>> for chipidea otg driver, simply replace a queue worker may not work,
> >>>>> as we have much more difference between the 2 configs.
> >>>>>
> >>>>
> >>>> Would you show more why chipidea can't work just replace the work item,
> >>>> and see if anything we still can improve for this framework?
> >>>
> >>> In real OTG, we need enable AVV irq,
> >>
> >> Enable and Handling AVV is platform stuff. In this framework, we are
> >> focus on how otg device manages host and gadget together, and the state
> >> machine when the related otg event occurs.
> >>
> >>> but for duel role, nobody care/handle,
> >>> there are much more resource required for OTG: timers, hnp polling,
> >>> otg test device handling...
> >>
> >> They are common things for fully OTG fsm, you can move them
> >> to common code (In fact, hnp polling handling is already common code).
> >>
> >>>
> >>> with current design, chipidea driver can support real OTG with its own
> >>> queue worker, or DRD with Roger's drd work item if config is correct.
> >>>
> >>> But improve something to work on a *wrong* config will make it complicated
> >>> and does not make much sense IMO.
> >>>
> >>
> >> What does above "config" you mean?
> >>
> >> If the configure is fully OTG, you can choose different state machine,
> >> eg otg_statemachine, if you find it is hard for chipidea to use this
> >> framework, just list the reason, and see if we can improve.
> >>
> >
> > Roger, after discussing with Jun off line, we think usb_otg_register
> > should return -ENOTSUPP if platform is OTG capabilities (HNP || SRP ||
> > ADP), since this patch set does not cover fully otg features, the users
>
> But this series isn't preventing full otg implementation. You can
> still do that via config->otg_work.
>
> I can modify the following condition to return -ENOTSUPP instead of
> defaulting to dual-role
>
> struct usb_otg *usb_otg_register(...)
> {
> ...
> 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");
> return -ENOTSUPP;
> }
> ...
> }
>

According to Jun, this framework still needs to improve (maybe small) even
using user work item for fully OTG, so the first step is let the drd
work well:)

--

Best Regards,
Peter Chen

2016-04-28 08:01:50

by Roger Quadros

[permalink] [raw]
Subject: Re: [PATCH v6 07/12] usb: otg: add OTG/dual-role core

On 28/04/16 04:54, Peter Chen wrote:
> On Wed, Apr 27, 2016 at 01:59:44PM +0300, Roger Quadros wrote:
>> Hi,
>>
>> On 27/04/16 06:15, Peter Chen wrote:
>>> On Tue, Apr 26, 2016 at 04:21:07PM +0800, Peter Chen wrote:
>>>> On Tue, Apr 26, 2016 at 07:00:22AM +0000, Jun Li wrote:
>>>>> Hi
>>>>>
>>>>>> -----Original Message-----
>>>>>> From: Peter Chen [mailto:[email protected]]
>>>>>> Sent: Tuesday, April 26, 2016 2:28 PM
>>>>>> To: Jun Li <[email protected]>
>>>>>> Cc: Roger Quadros <[email protected]>; [email protected];
>>>>>> [email protected]; [email protected]; [email protected];
>>>>>> [email protected]; [email protected];
>>>>>> [email protected]; [email protected]; [email protected];
>>>>>> [email protected]; [email protected]; [email protected];
>>>>>> [email protected]; [email protected]
>>>>>> Subject: Re: [PATCH v6 07/12] usb: otg: add OTG/dual-role core
>>>>>>
>>>>>> On Tue, Apr 26, 2016 at 05:11:36AM +0000, Jun Li wrote:
>>>>>>> Hi
>>>>>>>
>>>>>>>> -----Original Message-----
>>>>>>>> From: Peter Chen [mailto:[email protected]]
>>>>>>>> Sent: Tuesday, April 26, 2016 11:47 AM
>>>>>>>> To: Jun Li <[email protected]>
>>>>>>>> Cc: Roger Quadros <[email protected]>; [email protected];
>>>>>>>> [email protected]; [email protected];
>>>>>>>> [email protected]; [email protected];
>>>>>>>> [email protected]; [email protected];
>>>>>>>> [email protected]; [email protected]; [email protected];
>>>>>>>> [email protected]; [email protected];
>>>>>>>> [email protected]; [email protected]
>>>>>>>> Subject: Re: [PATCH v6 07/12] usb: otg: add OTG/dual-role core
>>>>>>>>
>>>>>>>> On Tue, Apr 26, 2016 at 02:07:56AM +0000, Jun Li wrote:
>>>>>>>>>> +struct usb_otg *usb_otg_register(struct device *dev,
>>>>>>>>>> + struct usb_otg_config *config) {
>>>>>>>>>> + struct usb_otg *otg;
>>>>>>>>>> + struct otg_wait_data *wait;
>>>>>>>>>> + int ret = 0;
>>>>>>>>>> +
>>>>>>>>>> + if (!dev || !config || !config->fsm_ops)
>>>>>>>>>> + return ERR_PTR(-EINVAL);
>>>>>>>>>> +
>>>>>>>>>> + /* already in list? */
>>>>>>>>>> + mutex_lock(&otg_list_mutex);
>>>>>>>>>> + if (usb_otg_get_data(dev)) {
>>>>>>>>>> + dev_err(dev, "otg: %s: device already in otg list\n",
>>>>>>>>>> + __func__);
>>>>>>>>>> + ret = -EINVAL;
>>>>>>>>>> + goto unlock;
>>>>>>>>>> + }
>>>>>>>>>> +
>>>>>>>>>> + /* allocate and add to list */
>>>>>>>>>> + otg = kzalloc(sizeof(*otg), GFP_KERNEL);
>>>>>>>>>> + if (!otg) {
>>>>>>>>>> + ret = -ENOMEM;
>>>>>>>>>> + goto unlock;
>>>>>>>>>> + }
>>>>>>>>>> +
>>>>>>>>>> + otg->dev = dev;
>>>>>>>>>> + otg->caps = config->otg_caps;
>>>>>>>>>> +
>>>>>>>>>> + if ((otg->caps->hnp_support || otg->caps->srp_support ||
>>>>>>>>>> + otg->caps->adp_support) && !config->otg_work)
>>>>>>>>>> + dev_info(dev, "otg: limiting to dual-role\n");
>>>>>>>>>
>>>>>>>>> dev_err, this should be an error.
>>>>>>>>
>>>>>>>> The condition may be wrong, but it is an information to show that
>>>>>>>> current OTG is dual-role.
>>>>>>>
>>>>>>> This should not happen in any correct design, I even doubt if we
>>>>>>> should try to continue by "downgrade" it to be duel role, currently
>>>>>>> the only example user is dual role, so doing like this can't be tested
>>>>>>> by real case, this downgrade is not so easy like we image, at least
>>>>>>> for chipidea otg driver, simply replace a queue worker may not work,
>>>>>>> as we have much more difference between the 2 configs.
>>>>>>>
>>>>>>
>>>>>> Would you show more why chipidea can't work just replace the work item,
>>>>>> and see if anything we still can improve for this framework?
>>>>>
>>>>> In real OTG, we need enable AVV irq,
>>>>
>>>> Enable and Handling AVV is platform stuff. In this framework, we are
>>>> focus on how otg device manages host and gadget together, and the state
>>>> machine when the related otg event occurs.
>>>>
>>>>> but for duel role, nobody care/handle,
>>>>> there are much more resource required for OTG: timers, hnp polling,
>>>>> otg test device handling...
>>>>
>>>> They are common things for fully OTG fsm, you can move them
>>>> to common code (In fact, hnp polling handling is already common code).
>>>>
>>>>>
>>>>> with current design, chipidea driver can support real OTG with its own
>>>>> queue worker, or DRD with Roger's drd work item if config is correct.
>>>>>
>>>>> But improve something to work on a *wrong* config will make it complicated
>>>>> and does not make much sense IMO.
>>>>>
>>>>
>>>> What does above "config" you mean?
>>>>
>>>> If the configure is fully OTG, you can choose different state machine,
>>>> eg otg_statemachine, if you find it is hard for chipidea to use this
>>>> framework, just list the reason, and see if we can improve.
>>>>
>>>
>>> Roger, after discussing with Jun off line, we think usb_otg_register
>>> should return -ENOTSUPP if platform is OTG capabilities (HNP || SRP ||
>>> ADP), since this patch set does not cover fully otg features, the users
>>
>> But this series isn't preventing full otg implementation. You can
>> still do that via config->otg_work.
>>
>> I can modify the following condition to return -ENOTSUPP instead of
>> defaulting to dual-role
>>
>> struct usb_otg *usb_otg_register(...)
>> {
>> ...
>> 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");
>> return -ENOTSUPP;
>> }
>> ...
>> }
>>
>
> According to Jun, this framework still needs to improve (maybe small) even
> using user work item for fully OTG, so the first step is let the drd
> work well:)
>
Agreed. Until one OTG implementation is tested we can't really say it works :).

cheers,
-roger

2016-04-28 09:55:21

by Roger Quadros

[permalink] [raw]
Subject: Re: [PATCH v6 09/12] usb: gadget: udc: adapt to OTG core

Hi,

On 27/04/16 14:22, Roger Quadros wrote:
> On 26/04/16 03:07, Jun Li wrote:
>> Hi
>>
>>> -----Original Message-----
>>> From: Roger Quadros [mailto:[email protected]]
>>> Sent: Monday, April 25, 2016 10:04 PM
>>> To: Jun Li <[email protected]>; [email protected]; [email protected];
>>> [email protected]; [email protected]
>>> Cc: [email protected]; [email protected];
>>> [email protected]; [email protected]; [email protected];
>>> [email protected]; [email protected]; [email protected];
>>> [email protected]; [email protected]
>>> Subject: Re: [PATCH v6 09/12] usb: gadget: udc: adapt to OTG core
>>>
>>> Hi,
>>>
>>> On 21/04/16 09:38, Jun Li wrote:
>>>> Hi,
>>>>
>>>> ...
>>>>>
>>>>> /**
>>>>> + * usb_gadget_start - start the usb gadget controller and connect to
>>>>> +bus
>>>>> + * @gadget: the gadget device to start
>>>>> + *
>>>>> + * This is external API for use by OTG core.
>>>>> + *
>>>>> + * Start the usb device controller and connect to bus (enable pull).
>>>>> + */
>>>>> +static int usb_gadget_start(struct usb_gadget *gadget) {
>>>>> + int ret;
>>>>> + struct usb_udc *udc = NULL;
>>>>> +
>>>>> + dev_dbg(&gadget->dev, "%s\n", __func__);
>>>>> + mutex_lock(&udc_lock);
>>>>> + list_for_each_entry(udc, &udc_list, list)
>>>>> + if (udc->gadget == gadget)
>>>>> + goto found;
>>>>> +
>>>>> + dev_err(gadget->dev.parent, "%s: gadget not registered.\n",
>>>>> + __func__);
>>>>> + mutex_unlock(&udc_lock);
>>>>> + return -EINVAL;
>>>>> +
>>>>> +found:
>>>>> + ret = usb_gadget_udc_start(udc);
>>>>> + if (ret)
>>>>> + dev_err(&udc->dev, "USB Device Controller didn't start: %d\n",
>>>>> + ret);
>>>>> + else
>>>>> + usb_udc_connect_control(udc);
>>>>
>>>> For drd, it's fine, but for real otg, gadget connect should be done by
>>>> loc_conn() instead of gadget start.
>>>
>>> It is upto the OTG state machine to call gadget_start() when it needs to
>>> connect to the bus (i.e. loc_conn()). I see no point in calling gadget
>>> start before.
>>>
>>> Do you see any issue in doing so?
>>
>> This is what OTG state machine does:
>> case OTG_STATE_B_PERIPHERAL:
>> otg_chrg_vbus(otg, 0);
>> otg_loc_sof(otg, 0);
>> otg_set_protocol(fsm, PROTO_GADGET);
>> otg_loc_conn(otg, 1);
>> break;

On second thoughts, after seen the OTG state machine.
otg_set_protocol(fsm, PROTO_GADGET); is always followed by otg_loc_conn(otg, 1);
And whenever protocol changes to anything other the PROTO_GADGET, we use
otg_loc_conn(otg, 0);

So otg_loc_conn seems redundant. Can we just get rid of it?

usb_gadget_start() implies that gadget controller starts up and enables pull.
usb_gadget_stop() implies that gadget controller disables pull and stops.


Can you please explain why just these 2 APIs are not sufficient for full OTG?

Do we want anything to happen between gadget controller start/stop and
pull on/off?

cheers,
-roger

>>
>> You intend to abstract something common in this api when start gadget,
>> which should be called by otg_set_protocol(fsm, PROTO_GADGET); and
>> drd_set_protocol(fsm, PROTO_GADGET); right?
>>
>> So you may move usb_udc_connect_control(IMO usb_gadget_connect()
>> is better)out of usb_gadget_start(), then for drd:
>>
>> case OTG_STATE_B_PERIPHERAL:
>> drd_set_protocol(fsm, PROTO_GADGET);
>> otg_drv_vbus(otg, 0);
>> usb_gadget_connect();
>
> OK. I understand now. I'll implement your suggestion. Thanks.
>
> cheers,
> -roger
>
>>>>
>>>>> +
>>>>> + mutex_unlock(&udc_lock);
>>>>> +
>>>>> + return ret;
>>>>> +}
>>>>> +
>>>>> +/**
>>>>> + * usb_gadget_stop - disconnect from bus and stop the usb gadget
>>>>> + * @gadget: The gadget device we want to stop
>>>>> + *
>>>>> + * This is external API for use by OTG core.
>>>>> + *
>>>>> + * Disconnect from the bus (disable pull) and stop the
>>>>> + * gadget controller.
>>>>> + */
>>>>> +static int usb_gadget_stop(struct usb_gadget *gadget) {
>>>>> + struct usb_udc *udc = NULL;
>>>>> +
>>>>> + dev_dbg(&gadget->dev, "%s\n", __func__);
>>>>> + mutex_lock(&udc_lock);
>>>>> + list_for_each_entry(udc, &udc_list, list)
>>>>> + if (udc->gadget == gadget)
>>>>> + goto found;
>>>>> +
>>>>> + dev_err(gadget->dev.parent, "%s: gadget not registered.\n",
>>>>> + __func__);
>>>>> + mutex_unlock(&udc_lock);
>>>>> + return -EINVAL;
>>>>> +
>>>>> +found:
>>>>> + usb_gadget_disconnect(udc->gadget);
>>>>
>>>> Likewise, gadget disconnect also should be done by loc_conn() instead
>>>> of gadget stop.
>>>>
>>>>> + udc->driver->disconnect(udc->gadget);
>>>>> + usb_gadget_udc_stop(udc);
>>>>> + mutex_unlock(&udc_lock);
>>>>> +
>>>>> + return 0;
>>>>> +}
>>>>> +
>>>>
>>>> Li Jun
>>>>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-usb" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html
>

2016-04-28 10:43:49

by Jun Li

[permalink] [raw]
Subject: RE: [PATCH v6 09/12] usb: gadget: udc: adapt to OTG core

Hi

> -----Original Message-----
> From: Roger Quadros [mailto:[email protected]]
> Sent: Thursday, April 28, 2016 5:55 PM
> To: Jun Li <[email protected]>; [email protected]; [email protected];
> [email protected]; [email protected]
> Cc: [email protected]; [email protected];
> [email protected]; [email protected]; [email protected];
> [email protected]; [email protected]; [email protected];
> [email protected]; [email protected]
> Subject: Re: [PATCH v6 09/12] usb: gadget: udc: adapt to OTG core
>
> Hi,
>
> On 27/04/16 14:22, Roger Quadros wrote:
> > On 26/04/16 03:07, Jun Li wrote:
> >> Hi
> >>
> >>> -----Original Message-----
> >>> From: Roger Quadros [mailto:[email protected]]
> >>> Sent: Monday, April 25, 2016 10:04 PM
> >>> To: Jun Li <[email protected]>; [email protected];
> >>> [email protected]; [email protected];
> >>> [email protected]
> >>> Cc: [email protected]; [email protected];
> >>> [email protected]; [email protected];
> >>> [email protected]; [email protected];
> >>> [email protected]; [email protected];
> >>> [email protected]; [email protected]
> >>> Subject: Re: [PATCH v6 09/12] usb: gadget: udc: adapt to OTG core
> >>>
> >>> Hi,
> >>>
> >>> On 21/04/16 09:38, Jun Li wrote:
> >>>> Hi,
> >>>>
> >>>> ...
> >>>>>
> >>>>> /**
> >>>>> + * usb_gadget_start - start the usb gadget controller and connect
> >>>>> +to bus
> >>>>> + * @gadget: the gadget device to start
> >>>>> + *
> >>>>> + * This is external API for use by OTG core.
> >>>>> + *
> >>>>> + * Start the usb device controller and connect to bus (enable pull).
> >>>>> + */
> >>>>> +static int usb_gadget_start(struct usb_gadget *gadget) {
> >>>>> + int ret;
> >>>>> + struct usb_udc *udc = NULL;
> >>>>> +
> >>>>> + dev_dbg(&gadget->dev, "%s\n", __func__);
> >>>>> + mutex_lock(&udc_lock);
> >>>>> + list_for_each_entry(udc, &udc_list, list)
> >>>>> + if (udc->gadget == gadget)
> >>>>> + goto found;
> >>>>> +
> >>>>> + dev_err(gadget->dev.parent, "%s: gadget not registered.\n",
> >>>>> + __func__);
> >>>>> + mutex_unlock(&udc_lock);
> >>>>> + return -EINVAL;
> >>>>> +
> >>>>> +found:
> >>>>> + ret = usb_gadget_udc_start(udc);
> >>>>> + if (ret)
> >>>>> + dev_err(&udc->dev, "USB Device Controller didn't
> start: %d\n",
> >>>>> + ret);
> >>>>> + else
> >>>>> + usb_udc_connect_control(udc);
> >>>>
> >>>> For drd, it's fine, but for real otg, gadget connect should be done
> >>>> by
> >>>> loc_conn() instead of gadget start.
> >>>
> >>> It is upto the OTG state machine to call gadget_start() when it
> >>> needs to connect to the bus (i.e. loc_conn()). I see no point in
> >>> calling gadget start before.
> >>>
> >>> Do you see any issue in doing so?
> >>
> >> This is what OTG state machine does:
> >> case OTG_STATE_B_PERIPHERAL:
> >> otg_chrg_vbus(otg, 0);
> >> otg_loc_sof(otg, 0);
> >> otg_set_protocol(fsm, PROTO_GADGET);
> >> otg_loc_conn(otg, 1);
> >> break;
>
> On second thoughts, after seen the OTG state machine.
> otg_set_protocol(fsm, PROTO_GADGET); is always followed by
> otg_loc_conn(otg, 1); And whenever protocol changes to anything other the
> PROTO_GADGET, we use otg_loc_conn(otg, 0);
>
> So otg_loc_conn seems redundant. Can we just get rid of it?
>
> usb_gadget_start() implies that gadget controller starts up and enables
> pull.
> usb_gadget_stop() implies that gadget controller disables pull and stops.
>
>
> Can you please explain why just these 2 APIs are not sufficient for full
> OTG?
>
> Do we want anything to happen between gadget controller start/stop and
> pull on/off?

"loc_conn" is a standard output parameter in OTG spec, it deserves
a separate api, yes, current implementation of OTG state machine code
seems allow you to combine the 2 things into one, but don't do that,
because they do not always happen together, e.g. for peripheral only
B device (also a part OTG spec: section 7.3), will be fixed in gadget
mode, but it will do gadget connect and disconnect in its diff states,
so, to make the framework common, let's keep them separated.

Li Jun

>
> cheers,
> -roger
>
> >>
> >> You intend to abstract something common in this api when start
> >> gadget, which should be called by otg_set_protocol(fsm,
> >> PROTO_GADGET); and drd_set_protocol(fsm, PROTO_GADGET); right?
> >>
> >> So you may move usb_udc_connect_control(IMO usb_gadget_connect() is
> >> better)out of usb_gadget_start(), then for drd:
> >>
> >> case OTG_STATE_B_PERIPHERAL:
> >> drd_set_protocol(fsm, PROTO_GADGET);
> >> otg_drv_vbus(otg, 0);
> >> usb_gadget_connect();
> >
> > OK. I understand now. I'll implement your suggestion. Thanks.
> >
> > cheers,
> > -roger
> >
> >>>>
> >>>>> +
> >>>>> + mutex_unlock(&udc_lock);
> >>>>> +
> >>>>> + return ret;
> >>>>> +}
> >>>>> +
> >>>>> +/**
> >>>>> + * usb_gadget_stop - disconnect from bus and stop the usb gadget
> >>>>> + * @gadget: The gadget device we want to stop
> >>>>> + *
> >>>>> + * This is external API for use by OTG core.
> >>>>> + *
> >>>>> + * Disconnect from the bus (disable pull) and stop the
> >>>>> + * gadget controller.
> >>>>> + */
> >>>>> +static int usb_gadget_stop(struct usb_gadget *gadget) {
> >>>>> + struct usb_udc *udc = NULL;
> >>>>> +
> >>>>> + dev_dbg(&gadget->dev, "%s\n", __func__);
> >>>>> + mutex_lock(&udc_lock);
> >>>>> + list_for_each_entry(udc, &udc_list, list)
> >>>>> + if (udc->gadget == gadget)
> >>>>> + goto found;
> >>>>> +
> >>>>> + dev_err(gadget->dev.parent, "%s: gadget not registered.\n",
> >>>>> + __func__);
> >>>>> + mutex_unlock(&udc_lock);
> >>>>> + return -EINVAL;
> >>>>> +
> >>>>> +found:
> >>>>> + usb_gadget_disconnect(udc->gadget);
> >>>>
> >>>> Likewise, gadget disconnect also should be done by loc_conn()
> >>>> instead of gadget stop.
> >>>>
> >>>>> + udc->driver->disconnect(udc->gadget);
> >>>>> + usb_gadget_udc_stop(udc);
> >>>>> + mutex_unlock(&udc_lock);
> >>>>> +
> >>>>> + return 0;
> >>>>> +}
> >>>>> +
> >>>>
> >>>> Li Jun
> >>>>
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-usb"
> > in the body of a message to [email protected] More majordomo
> > info at http://vger.kernel.org/majordomo-info.html
> >

2016-04-28 12:23:00

by Roger Quadros

[permalink] [raw]
Subject: Re: [PATCH v6 09/12] usb: gadget: udc: adapt to OTG core



On 28/04/16 13:23, Jun Li wrote:
> Hi
>
>> -----Original Message-----
>> From: Roger Quadros [mailto:[email protected]]
>> Sent: Thursday, April 28, 2016 5:55 PM
>> To: Jun Li <[email protected]>; [email protected]; [email protected];
>> [email protected]; [email protected]
>> Cc: [email protected]; [email protected];
>> [email protected]; [email protected]; [email protected];
>> [email protected]; [email protected]; [email protected];
>> [email protected]; [email protected]
>> Subject: Re: [PATCH v6 09/12] usb: gadget: udc: adapt to OTG core
>>
>> Hi,
>>
>> On 27/04/16 14:22, Roger Quadros wrote:
>>> On 26/04/16 03:07, Jun Li wrote:
>>>> Hi
>>>>
>>>>> -----Original Message-----
>>>>> From: Roger Quadros [mailto:[email protected]]
>>>>> Sent: Monday, April 25, 2016 10:04 PM
>>>>> To: Jun Li <[email protected]>; [email protected];
>>>>> [email protected]; [email protected];
>>>>> [email protected]
>>>>> Cc: [email protected]; [email protected];
>>>>> [email protected]; [email protected];
>>>>> [email protected]; [email protected];
>>>>> [email protected]; [email protected];
>>>>> [email protected]; [email protected]
>>>>> Subject: Re: [PATCH v6 09/12] usb: gadget: udc: adapt to OTG core
>>>>>
>>>>> Hi,
>>>>>
>>>>> On 21/04/16 09:38, Jun Li wrote:
>>>>>> Hi,
>>>>>>
>>>>>> ...
>>>>>>>
>>>>>>> /**
>>>>>>> + * usb_gadget_start - start the usb gadget controller and connect
>>>>>>> +to bus
>>>>>>> + * @gadget: the gadget device to start
>>>>>>> + *
>>>>>>> + * This is external API for use by OTG core.
>>>>>>> + *
>>>>>>> + * Start the usb device controller and connect to bus (enable pull).
>>>>>>> + */
>>>>>>> +static int usb_gadget_start(struct usb_gadget *gadget) {
>>>>>>> + int ret;
>>>>>>> + struct usb_udc *udc = NULL;
>>>>>>> +
>>>>>>> + dev_dbg(&gadget->dev, "%s\n", __func__);
>>>>>>> + mutex_lock(&udc_lock);
>>>>>>> + list_for_each_entry(udc, &udc_list, list)
>>>>>>> + if (udc->gadget == gadget)
>>>>>>> + goto found;
>>>>>>> +
>>>>>>> + dev_err(gadget->dev.parent, "%s: gadget not registered.\n",
>>>>>>> + __func__);
>>>>>>> + mutex_unlock(&udc_lock);
>>>>>>> + return -EINVAL;
>>>>>>> +
>>>>>>> +found:
>>>>>>> + ret = usb_gadget_udc_start(udc);
>>>>>>> + if (ret)
>>>>>>> + dev_err(&udc->dev, "USB Device Controller didn't
>> start: %d\n",
>>>>>>> + ret);
>>>>>>> + else
>>>>>>> + usb_udc_connect_control(udc);
>>>>>>
>>>>>> For drd, it's fine, but for real otg, gadget connect should be done
>>>>>> by
>>>>>> loc_conn() instead of gadget start.
>>>>>
>>>>> It is upto the OTG state machine to call gadget_start() when it
>>>>> needs to connect to the bus (i.e. loc_conn()). I see no point in
>>>>> calling gadget start before.
>>>>>
>>>>> Do you see any issue in doing so?
>>>>
>>>> This is what OTG state machine does:
>>>> case OTG_STATE_B_PERIPHERAL:
>>>> otg_chrg_vbus(otg, 0);
>>>> otg_loc_sof(otg, 0);
>>>> otg_set_protocol(fsm, PROTO_GADGET);
>>>> otg_loc_conn(otg, 1);
>>>> break;
>>
>> On second thoughts, after seen the OTG state machine.
>> otg_set_protocol(fsm, PROTO_GADGET); is always followed by
>> otg_loc_conn(otg, 1); And whenever protocol changes to anything other the
>> PROTO_GADGET, we use otg_loc_conn(otg, 0);
>>
>> So otg_loc_conn seems redundant. Can we just get rid of it?
>>
>> usb_gadget_start() implies that gadget controller starts up and enables
>> pull.
>> usb_gadget_stop() implies that gadget controller disables pull and stops.
>>
>>
>> Can you please explain why just these 2 APIs are not sufficient for full
>> OTG?
>>
>> Do we want anything to happen between gadget controller start/stop and
>> pull on/off?
>
> "loc_conn" is a standard output parameter in OTG spec, it deserves
> a separate api, yes, current implementation of OTG state machine code
> seems allow you to combine the 2 things into one, but don't do that,
> because they do not always happen together, e.g. for peripheral only
> B device (also a part OTG spec: section 7.3), will be fixed in gadget
> mode, but it will do gadget connect and disconnect in its diff states,
> so, to make the framework common, let's keep them separated.

I'm sorry but I didn't understand your comment about "it will do gadget
connect and disconnect in its diff states"

I am reading the OTG v2.0 specification and loc_conn is always true when
b_peripheral or a_peripheral is true and false otherwise.

loc_conn is just an internal state variable and it corresponds to our gadget_start/stop() state.

As per 7.4.2.3
"loc_conn
The ?local connect? (loc_conn) variable is TRUE when the local device has signaled that it is connected to
the bus. This variable is FALSE when the local device has signaled that it is disconnected from the bus"

Can you please point me in the specification if there is any place where loc_conn
is false and b_peripheral/a_peripheral is true?

cheers,
-roger