2014-11-25 13:15:35

by George Cherian

[permalink] [raw]
Subject: [PATCH 00/19] Add Support for USB DRD in AM437x

The series add DRD suport for AM437x.

The serires adds
- USB DRD Library
This Library facilitates in switching roles between HOST
and Device.
- DWC3 OTG driver.
This driver currently suports only the ID based switching


Felipe Balbi (2):
usb: dwc3: core: Adapt to named interrupts
arm: dts: am4372: Add named interrupt property for dwc3

George Cherian (17):
usb: common: drd-lib: Add DRD lib for USB.
usb: host xhci: fix up deallocation code
usb: host: xhci-plat: Add support to pass XHCI_DRD_SUPPORT quirk
usb: host xhci: Add XHCI_NEEDS_LHC_RESET quirk
usb: host: xhci-plat: Add support to pass XHCI_NEEDS_LHC_RESET quirk
usb: dwc3: host: Pass the XHCI_DRD_SUPPORT and XHCI_NEEDS_LHC_RESET
quirk
usb: host: xhci: Adapt xhci to use usb drd library
usb: dwc3: core: Add dwc3_drd_helper function
usb: dwc3: dwc3-omap: Make the wrapper interrupt shared
usb: dwc3: Add seperate dwc3_gadget object to support gadget release
usb: dwc3: gadget: Adapt gadget to drd library
usb: dwc3: core: Add DWC3 OTG specific register defines
usb: dwc3: otg: Add the initial otg driver for dwc3.
arm: dts: omap5: Add named interrupt property for dwc3
arm: dts: dra7: Add named interrupt property for dwc3
arm: dts: exynos5250: Add named interrupt property for dwc3
arm: dts: am43x evms: Make usb1 as OTG

arch/arm/boot/dts/am4372.dtsi | 18 +-
arch/arm/boot/dts/am437x-gp-evm.dts | 2 +-
arch/arm/boot/dts/am437x-sk-evm.dts | 2 +-
arch/arm/boot/dts/am43x-epos-evm.dts | 2 +-
arch/arm/boot/dts/dra7.dtsi | 28 ++-
arch/arm/boot/dts/exynos5250.dtsi | 7 +-
arch/arm/boot/dts/omap5.dtsi | 7 +-
drivers/usb/Kconfig | 15 ++
drivers/usb/common/Makefile | 1 +
drivers/usb/common/drd-lib.c | 346 +++++++++++++++++++++++++++++++++++
drivers/usb/dwc3/Makefile | 4 +
drivers/usb/dwc3/core.c | 27 ++-
drivers/usb/dwc3/core.h | 93 +++++++++-
drivers/usb/dwc3/dwc3-omap.c | 4 +-
drivers/usb/dwc3/ep0.c | 35 ++--
drivers/usb/dwc3/gadget.c | 211 +++++++++++++++------
drivers/usb/dwc3/gadget.h | 1 +
drivers/usb/dwc3/host.c | 2 +
drivers/usb/dwc3/otg.c | 126 +++++++++++++
drivers/usb/host/xhci-plat.c | 22 +++
drivers/usb/host/xhci.c | 27 ++-
drivers/usb/host/xhci.h | 2 +
include/linux/usb/drd.h | 77 ++++++++
include/linux/usb/xhci_pdriver.h | 2 +
24 files changed, 962 insertions(+), 99 deletions(-)
create mode 100644 drivers/usb/common/drd-lib.c
create mode 100644 drivers/usb/dwc3/otg.c
create mode 100644 include/linux/usb/drd.h

--
1.8.3.1


2014-11-25 13:15:51

by George Cherian

[permalink] [raw]
Subject: [PATCH 03/19] usb: host: xhci-plat: Add support to pass XHCI_DRD_SUPPORT quirk

Extend the platform data to pass XHCI_DRD_SUPPORT quirk to the xhci driver.

Signed-off-by: George Cherian <[email protected]>
---
drivers/usb/host/xhci-plat.c | 4 ++++
include/linux/usb/xhci_pdriver.h | 1 +
2 files changed, 5 insertions(+)

diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c
index 3d78b0c..2c42273 100644
--- a/drivers/usb/host/xhci-plat.c
+++ b/drivers/usb/host/xhci-plat.c
@@ -27,6 +27,10 @@ static struct hc_driver __read_mostly xhci_plat_hc_driver;

static void xhci_plat_quirks(struct device *dev, struct xhci_hcd *xhci)
{
+ struct usb_xhci_pdata *pdata = dev_get_platdata(dev);
+
+ if (pdata->usb_drd_support)
+ xhci->quirks |= XHCI_DRD_SUPPORT;
/*
* As of now platform drivers don't provide MSI support so we ensure
* here that the generic code does not try to make a pci_dev from our
diff --git a/include/linux/usb/xhci_pdriver.h b/include/linux/usb/xhci_pdriver.h
index 376654b..539c2d8 100644
--- a/include/linux/usb/xhci_pdriver.h
+++ b/include/linux/usb/xhci_pdriver.h
@@ -22,6 +22,7 @@
*/
struct usb_xhci_pdata {
unsigned usb3_lpm_capable:1;
+ unsigned usb_drd_support:1;
};

#endif /* __USB_CORE_XHCI_PDRIVER_H */
--
1.8.3.1

2014-11-25 13:15:56

by George Cherian

[permalink] [raw]
Subject: [PATCH 04/19] usb: host xhci: Add XHCI_NEEDS_LHC_RESET quirk

This adds XHCI_NEEDS_LHC_RESET quirk, to make sure only Light Host Reset
is done during xhci_reset(). This is mainly useful when we switch roles
HOST to Device mode and viceversa.

The DWC3 IP shares internal RAM for both HOST and Device specific registers.
So while switching roles between HOST and Device modes, it's advbised to do
a LIGHT HC reset else the already configured global registers of the DWC3 IP
gets re-initialized.

Signed-off-by: George Cherian <[email protected]>
---
drivers/usb/host/xhci.c | 5 +++--
drivers/usb/host/xhci.h | 1 +
2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index d4196f8..5dabf9a 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -170,11 +170,12 @@ int xhci_reset(struct xhci_hcd *xhci)

xhci_dbg_trace(xhci, trace_xhci_dbg_init, "// Reset the HC");
command = readl(&xhci->op_regs->command);
- command |= CMD_RESET;
+ command |= (xhci->quirks & XHCI_NEEDS_LHC_RESET) ? CMD_LRESET : CMD_RESET;
writel(command, &xhci->op_regs->command);

ret = xhci_handshake(xhci, &xhci->op_regs->command,
- CMD_RESET, 0, 10 * 1000 * 1000);
+ (xhci->quirks & XHCI_NEEDS_LHC_RESET) ? CMD_LRESET : CMD_RESET,
+ 0, 10 * 1000 * 1000);
if (ret)
return ret;

diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 2248058..1b14b09 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -1561,6 +1561,7 @@ struct xhci_hcd {
/* For controllers with a broken beyond repair streams implementation */
#define XHCI_BROKEN_STREAMS (1 << 19)
#define XHCI_DRD_SUPPORT (1 << 20)
+#define XHCI_NEEDS_LHC_RESET (1 << 21)
unsigned int num_active_eps;
unsigned int limit_active_eps;
/* There are two roothubs to keep track of bus suspend info for */
--
1.8.3.1

2014-11-25 13:16:08

by George Cherian

[permalink] [raw]
Subject: [PATCH 09/19] usb: dwc3: dwc3-omap: Make the wrapper interrupt shared

OTG interrupt and wrapper is shared

Signed-off-by: George Cherian <[email protected]>
---
drivers/usb/dwc3/dwc3-omap.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c
index 172d64e..f99e2ca 100644
--- a/drivers/usb/dwc3/dwc3-omap.c
+++ b/drivers/usb/dwc3/dwc3-omap.c
@@ -525,8 +525,8 @@ static int dwc3_omap_probe(struct platform_device *pdev)
reg = dwc3_omap_readl(omap->base, USBOTGSS_SYSCONFIG);
omap->dma_status = !!(reg & USBOTGSS_SYSCONFIG_DMADISABLE);

- ret = devm_request_irq(dev, omap->irq, dwc3_omap_interrupt, 0,
- "dwc3-omap", omap);
+ ret = devm_request_irq(dev, omap->irq, dwc3_omap_interrupt, IRQF_SHARED,
+ "dwc3-omap", omap);
if (ret) {
dev_err(dev, "failed to request IRQ #%d --> %d\n",
omap->irq, ret);
--
1.8.3.1

2014-11-25 13:16:14

by George Cherian

[permalink] [raw]
Subject: [PATCH 08/19] usb: dwc3: core: Add dwc3_drd_helper function

This helper function, facilitates to re-initialize the event buffers.
It re-initilizes the event buffers while switching role from
HOST to DEVICE mode.

The DWC3 IP shares internal RAM for both HOST and Device specific registers.
So while switching roles from HOST to Device modes, it's required to
re-initialize the EVENT buffer registers for the Device mode to continue
work properly. dwc3_event_buffers_setup() is exported out from core.c via
wrapper dwc3_core_gadget_helper() which will be invoked from dwc3 otg driver.

Signed-off-by: George Cherian <[email protected]>
---
drivers/usb/dwc3/core.c | 5 +++++
drivers/usb/dwc3/core.h | 1 +
2 files changed, 6 insertions(+)

diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 25ddc39..fadd767 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -254,6 +254,11 @@ static void dwc3_event_buffers_cleanup(struct dwc3 *dwc)
}
}

+int dwc3_core_gadget_helper(struct dwc3 *dwc)
+{
+ return dwc3_event_buffers_setup(dwc);
+}
+
static int dwc3_alloc_scratch_buffers(struct dwc3 *dwc)
{
if (!dwc->has_hibernation)
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 4bb9aa6..6b38223 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -981,6 +981,7 @@ struct dwc3_gadget_ep_cmd_params {
/* prototypes */
void dwc3_set_mode(struct dwc3 *dwc, u32 mode);
int dwc3_gadget_resize_tx_fifos(struct dwc3 *dwc);
+int dwc3_core_gadget_helper(struct dwc3 *dwc);

#if IS_ENABLED(CONFIG_USB_DWC3_HOST) || IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)
int dwc3_host_init(struct dwc3 *dwc);
--
1.8.3.1

2014-11-25 13:16:26

by George Cherian

[permalink] [raw]
Subject: [PATCH 10/19] usb: dwc3: core: Adapt to named interrupts

From: Felipe Balbi <[email protected]>

Add support to use interrupt names,

Following are the interrupt names

Peripheral Interrupt - peripheral
HOST Interrupt - host
OTG Interrupt - otg

Signed-off-by: Felipe Balbi <[email protected]>
Signed-off-by: George Cherian <[email protected]>
---
drivers/usb/dwc3/core.c | 12 ++++++++++++
drivers/usb/dwc3/core.h | 7 +++++++
2 files changed, 19 insertions(+)

diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index fadd767..dbd5589 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -760,6 +760,18 @@ static int dwc3_probe(struct platform_device *pdev)
dwc->xhci_resources[1].flags = res->flags;
dwc->xhci_resources[1].name = res->name;

+ dwc->otg_irq = platform_get_irq_byname(pdev, "otg");
+ if (!dwc->otg_irq)
+ dev_err(dev, "missing OTG IRQ\n");
+
+ dwc->gadget_irq = platform_get_irq_byname(pdev, "peripheral");
+ if (!dwc->gadget_irq)
+ dev_err(dev, "missing peripheral IRQ\n");
+
+ dwc->xhci_irq = platform_get_irq_byname(pdev, "host");
+ if (!dwc->xhci_irq)
+ dev_err(dev, "missing HOST IRQ\n");
+
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(dev, "missing memory resource\n");
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 6b38223..7c5ae37 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -651,6 +651,9 @@ struct dwc3_scratchpad_array {
* @maximum_speed: maximum speed requested (mainly for testing purposes)
* @revision: revision register contents
* @dr_mode: requested mode of operation
+ * @xhci_irq: IRQ number for XHCI IRQs
+ * @gadget_irq: IRQ number for Peripheral IRQs
+ * @otg_irq: IRQ number for OTG IRQs
* @usb2_phy: pointer to USB2 PHY
* @usb3_phy: pointer to USB3 PHY
* @usb2_generic_phy: pointer to USB2 PHY
@@ -747,6 +750,10 @@ struct dwc3 {

enum usb_dr_mode dr_mode;

+ int gadget_irq;
+ int xhci_irq;
+ int otg_irq;
+
/* used for suspend/resume */
u32 dcfg;
u32 gctl;
--
1.8.3.1

2014-11-25 13:16:33

by George Cherian

[permalink] [raw]
Subject: [PATCH 12/19] usb: dwc3: gadget: Adapt gadget to drd library

Adapt the dwc3 gadget to use drd library functions.
In prepration to support DRD on dwc3.

Signed-off-by: George Cherian <[email protected]>
---
drivers/usb/dwc3/gadget.c | 128 ++++++++++++++++++++++++++++++++++++----------
1 file changed, 100 insertions(+), 28 deletions(-)

diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 2c54d45..a75fae5 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -29,6 +29,7 @@

#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
+#include <linux/usb/drd.h>

#include "debug.h"
#include "core.h"
@@ -2681,6 +2682,89 @@ static irqreturn_t dwc3_interrupt(int irq, void *_dwc)
return ret;
}

+void dwc3_gadget_release(struct device *dev)
+{
+ struct usb_gadget *gadget = container_of(dev, struct usb_gadget, dev);
+ struct dwc3_gadget *dwc_gadget = gadget_to_dwc_gadget(gadget);
+ struct dwc3 *dwc = dwc_gadget->dwc;
+
+ dev_dbg(dev, "releasing '%s'\n", dev_name(dev));
+ dwc3_gadget_free_endpoints(dwc);
+ dma_free_coherent(dwc->dev, DWC3_EP0_BOUNCE_SIZE,
+ dwc->ep0_bounce, dwc->ep0_bounce_addr);
+ kfree(dwc->setup_buf);
+ dma_free_coherent(dwc->dev, sizeof(*dwc->ep0_trb),
+ dwc->ep0_trb, dwc->ep0_trb_addr);
+ dma_free_coherent(dwc->dev, sizeof(*dwc->ctrl_req),
+ dwc->ctrl_req, dwc->ctrl_req_addr);
+ usb_drd_unregister_udc(dwc->dev);
+ kfree(dwc_gadget);
+}
+
+int dwc3_gadget_setup(void *data)
+{
+ struct dwc3 *dwc = data;
+ struct dwc3_gadget *dwc_gadget;
+ struct usb_drd_gadget *drd_gadget;
+ struct usb_drd_setup *gadget_setup;
+ int ret;
+
+ drd_gadget = kzalloc(sizeof(*drd_gadget), GFP_KERNEL);
+ if (!drd_gadget) {
+ ret = -ENOMEM;
+ goto err1;
+ }
+
+ gadget_setup = kzalloc(sizeof(*gadget_setup), GFP_KERNEL);
+ if (!gadget_setup) {
+ ret = -ENOMEM;
+ goto err2;
+ }
+
+ dwc_gadget = kzalloc(sizeof(*dwc_gadget), GFP_KERNEL);
+ if (!dwc_gadget) {
+ ret = -ENOMEM;
+ goto err3;
+ }
+
+ drd_gadget->g_driver = dwc->gadget_driver;
+
+ /*
+ * Pass the DWC3 specific routines for
+ * switching roles to the drd library
+ */
+ gadget_setup->ll_start = NULL;
+ gadget_setup->ll_stop = NULL;
+ gadget_setup->ll_release = dwc3_gadget_release;
+ gadget_setup->data = (void *)dwc;
+ drd_gadget->gadget_setup = gadget_setup;
+
+ dwc_gadget->gadget.ops = &dwc3_gadget_ops;
+ dwc_gadget->gadget.max_speed = USB_SPEED_SUPER;
+ dwc_gadget->gadget.speed = USB_SPEED_UNKNOWN;
+ dwc_gadget->gadget.sg_supported = true;
+ dwc_gadget->gadget.name = "dwc3-gadget";
+ dwc_gadget->dwc = dwc;
+ drd_gadget->gadget = &dwc_gadget->gadget;
+
+ /*
+ * Per databook, DWC3 needs buffer size to be aligned to MaxPacketSize
+ * on ep out.
+ */
+ dwc_gadget->gadget.quirk_ep_out_aligned_size = true;
+ dwc->dwc_gadget = dwc_gadget;
+ usb_drd_register_udc(dwc->dev, drd_gadget);
+
+ return 0;
+
+err3:
+ kfree(gadget_setup);
+err2:
+ kfree(drd_gadget);
+err1:
+ return ret;
+}
+
/**
* dwc3_gadget_init - Initializes gadget related registers
* @dwc: pointer to our controller context structure
@@ -2690,7 +2774,6 @@ static irqreturn_t dwc3_interrupt(int irq, void *_dwc)
int dwc3_gadget_init(struct dwc3 *dwc)
{
int ret;
- struct dwc3_gadget *dwc_gadget;

dwc->ctrl_req = dma_alloc_coherent(dwc->dev, sizeof(*dwc->ctrl_req),
&dwc->ctrl_req_addr, GFP_KERNEL);
@@ -2723,24 +2806,9 @@ int dwc3_gadget_init(struct dwc3 *dwc)
goto err3;
}

- dwc_gadget = kzalloc(sizeof(*dwc_gadget), GFP_KERNEL);
- if (!dwc_gadget) {
- ret = -ENOMEM;
+ ret = dwc3_gadget_setup(dwc);
+ if (ret)
goto err3;
- }
-
- dwc_gadget->gadget.ops = &dwc3_gadget_ops;
- dwc_gadget->gadget.max_speed = USB_SPEED_SUPER;
- dwc_gadget->gadget.speed = USB_SPEED_UNKNOWN;
- dwc_gadget->gadget.sg_supported = true;
- dwc_gadget->gadget.name = "dwc3-gadget";
- dwc_gadget->dwc = dwc;
-
- /*
- * Per databook, DWC3 needs buffer size to be aligned to MaxPacketSize
- * on ep out.
- */
- dwc_gadget->gadget.quirk_ep_out_aligned_size = true;

/*
* REVISIT: Here we should clear all pending IRQs to be
@@ -2751,7 +2819,8 @@ int dwc3_gadget_init(struct dwc3 *dwc)
if (ret)
goto err4;

- ret = usb_add_gadget_udc(dwc->dev, &dwc_gadget->gadget);
+ ret = usb_add_gadget_udc_release(dwc->dev, &dwc->dwc_gadget->gadget,
+ dwc3_gadget_release);
if (ret) {
dev_err(dwc->dev, "failed to register udc\n");
goto err4;
@@ -2760,6 +2829,7 @@ int dwc3_gadget_init(struct dwc3 *dwc)
return 0;

err4:
+ usb_drd_unregister_udc(dwc->dev);
dwc3_gadget_free_endpoints(dwc);
dma_free_coherent(dwc->dev, DWC3_EP0_BOUNCE_SIZE,
dwc->ep0_bounce, dwc->ep0_bounce_addr);
@@ -2785,20 +2855,22 @@ void dwc3_gadget_exit(struct dwc3 *dwc)
{
struct dwc3_gadget *dwc_gadget = dwc->dwc_gadget;

- usb_del_gadget_udc(&dwc_gadget->gadget);
+ if (usb_drd_get_state(dwc->dev) & DRD_DEVICE_REGISTERED) {
+ usb_del_gadget_udc(&dwc_gadget->gadget);

- dwc3_gadget_free_endpoints(dwc);
+ dwc3_gadget_free_endpoints(dwc);

- dma_free_coherent(dwc->dev, DWC3_EP0_BOUNCE_SIZE,
- dwc->ep0_bounce, dwc->ep0_bounce_addr);
+ dma_free_coherent(dwc->dev, DWC3_EP0_BOUNCE_SIZE,
+ dwc->ep0_bounce, dwc->ep0_bounce_addr);

- kfree(dwc->setup_buf);
+ kfree(dwc->setup_buf);

- dma_free_coherent(dwc->dev, sizeof(*dwc->ep0_trb),
- dwc->ep0_trb, dwc->ep0_trb_addr);
+ dma_free_coherent(dwc->dev, sizeof(*dwc->ep0_trb),
+ dwc->ep0_trb, dwc->ep0_trb_addr);

- dma_free_coherent(dwc->dev, sizeof(*dwc->ctrl_req),
- dwc->ctrl_req, dwc->ctrl_req_addr);
+ dma_free_coherent(dwc->dev, sizeof(*dwc->ctrl_req),
+ dwc->ctrl_req, dwc->ctrl_req_addr);
+ }
}

int dwc3_gadget_suspend(struct dwc3 *dwc)
--
1.8.3.1

2014-11-25 13:16:40

by George Cherian

[permalink] [raw]
Subject: [PATCH 11/19] usb: dwc3: Add seperate dwc3_gadget object to support gadget release

With the current implementation it's impossible to release the gadget.
Add a separate dwc3_gadget object to dwc3 structure so that the same
can be freed during the gadget release.
This is in prepration to adapt dwc3 gadget driver to drd library.
DRD library uses usb_del/add_gadget_udc while switching roles between
HOST and DEVICE modes. If the usb_gadget is not released during usb_del_gadget_udc,
the subsequent usb_add_gadget_udc would try to initialize an already initialized
kobject. To avoid this make sure we have an easily freeable object.

Signed-off-by: George Cherian <[email protected]>
---
drivers/usb/dwc3/core.h | 7 ++-
drivers/usb/dwc3/ep0.c | 35 ++++++++------
drivers/usb/dwc3/gadget.c | 113 ++++++++++++++++++++++++++++++----------------
drivers/usb/dwc3/gadget.h | 1 +
4 files changed, 101 insertions(+), 55 deletions(-)

diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 7c5ae37..7fbe736 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -626,6 +626,11 @@ struct dwc3_scratchpad_array {
__le64 dma_adr[DWC3_MAX_HIBER_SCRATCHBUFS];
};

+struct dwc3_gadget {
+ struct usb_gadget gadget;
+ struct dwc3 *dwc;
+};
+
/**
* struct dwc3 - representation of our controller
* @ctrl_req: usb control request which is used for ep0
@@ -736,7 +741,7 @@ struct dwc3 {
struct dwc3_event_buffer **ev_buffs;
struct dwc3_ep *eps[DWC3_ENDPOINTS_NUM];

- struct usb_gadget gadget;
+ struct dwc3_gadget *dwc_gadget;
struct usb_gadget_driver *gadget_driver;

struct usb_phy *usb2_phy;
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index baeedbd..e7409f1 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -109,6 +109,7 @@ static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep,
struct dwc3_request *req)
{
struct dwc3 *dwc = dep->dwc;
+ struct dwc3_gadget *dwc_gadget = dwc->dwc_gadget;

req->request.actual = 0;
req->request.status = -EINPROGRESS;
@@ -152,7 +153,7 @@ static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep,

direction = !dwc->ep0_expect_in;
dwc->delayed_status = false;
- usb_gadget_set_state(&dwc->gadget, USB_STATE_CONFIGURED);
+ usb_gadget_set_state(&dwc_gadget->gadget, USB_STATE_CONFIGURED);

if (dwc->ep0state == EP0_STATUS_PHASE)
__dwc3_ep0_do_control_status(dwc, dwc->eps[direction]);
@@ -391,6 +392,7 @@ static int dwc3_ep0_handle_feature(struct dwc3 *dwc,
struct usb_ctrlrequest *ctrl, int set)
{
struct dwc3_ep *dep;
+ struct dwc3_gadget *dwc_gadget = dwc->dwc_gadget;
u32 recip;
u32 wValue;
u32 wIndex;
@@ -401,7 +403,7 @@ static int dwc3_ep0_handle_feature(struct dwc3 *dwc,
wValue = le16_to_cpu(ctrl->wValue);
wIndex = le16_to_cpu(ctrl->wIndex);
recip = ctrl->bRequestType & USB_RECIP_MASK;
- state = dwc->gadget.state;
+ state = dwc_gadget->gadget.state;

switch (recip) {
case USB_RECIP_DEVICE:
@@ -499,7 +501,8 @@ static int dwc3_ep0_handle_feature(struct dwc3 *dwc,

static int dwc3_ep0_set_address(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
{
- enum usb_device_state state = dwc->gadget.state;
+ struct dwc3_gadget *dwc_gadget = dwc->dwc_gadget;
+ enum usb_device_state state = dwc_gadget->gadget.state;
u32 addr;
u32 reg;

@@ -521,26 +524,28 @@ static int dwc3_ep0_set_address(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
dwc3_writel(dwc->regs, DWC3_DCFG, reg);

if (addr)
- usb_gadget_set_state(&dwc->gadget, USB_STATE_ADDRESS);
+ usb_gadget_set_state(&dwc_gadget->gadget, USB_STATE_ADDRESS);
else
- usb_gadget_set_state(&dwc->gadget, USB_STATE_DEFAULT);
+ usb_gadget_set_state(&dwc_gadget->gadget, USB_STATE_DEFAULT);

return 0;
}

static int dwc3_ep0_delegate_req(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
{
+ struct dwc3_gadget *dwc_gadget = dwc->dwc_gadget;
int ret;

spin_unlock(&dwc->lock);
- ret = dwc->gadget_driver->setup(&dwc->gadget, ctrl);
+ ret = dwc->gadget_driver->setup(&dwc_gadget->gadget, ctrl);
spin_lock(&dwc->lock);
return ret;
}

static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
{
- enum usb_device_state state = dwc->gadget.state;
+ struct dwc3_gadget *dwc_gadget = dwc->dwc_gadget;
+ enum usb_device_state state = dwc_gadget->gadget.state;
u32 cfg;
int ret;
u32 reg;
@@ -564,8 +569,8 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
* to change the state on the next usb_ep_queue()
*/
if (ret == 0)
- usb_gadget_set_state(&dwc->gadget,
- USB_STATE_CONFIGURED);
+ usb_gadget_set_state(&dwc_gadget->gadget,
+ USB_STATE_CONFIGURED);

/*
* Enable transition to U1/U2 state when
@@ -583,8 +588,8 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
case USB_STATE_CONFIGURED:
ret = dwc3_ep0_delegate_req(dwc, ctrl);
if (!cfg && !ret)
- usb_gadget_set_state(&dwc->gadget,
- USB_STATE_ADDRESS);
+ usb_gadget_set_state(&dwc_gadget->gadget,
+ USB_STATE_ADDRESS);
break;
default:
ret = -EINVAL;
@@ -639,7 +644,8 @@ static void dwc3_ep0_set_sel_cmpl(struct usb_ep *ep, struct usb_request *req)
static int dwc3_ep0_set_sel(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
{
struct dwc3_ep *dep;
- enum usb_device_state state = dwc->gadget.state;
+ struct dwc3_gadget *dwc_gadget = dwc->dwc_gadget;
+ enum usb_device_state state = dwc_gadget->gadget.state;
u16 wLength;
u16 wValue;

@@ -918,6 +924,7 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
struct dwc3_ep *dep, struct dwc3_request *req)
{
int ret;
+ struct dwc3_gadget *dwc_gadget = dwc->dwc_gadget;

req->direction = !!dep->number;

@@ -930,7 +937,7 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
u32 transfer_size;
u32 maxpacket;

- ret = usb_gadget_map_request(&dwc->gadget, &req->request,
+ ret = usb_gadget_map_request(&dwc_gadget->gadget, &req->request,
dep->number);
if (ret) {
dev_dbg(dwc->dev, "failed to map request\n");
@@ -953,7 +960,7 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
dwc->ep0_bounce_addr, transfer_size,
DWC3_TRBCTL_CONTROL_DATA);
} else {
- ret = usb_gadget_map_request(&dwc->gadget, &req->request,
+ ret = usb_gadget_map_request(&dwc_gadget->gadget, &req->request,
dep->number);
if (ret) {
dev_dbg(dwc->dev, "failed to map request\n");
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index f03b136..2c54d45 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -234,6 +234,7 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
int status)
{
struct dwc3 *dwc = dep->dwc;
+ struct dwc3_gadget *dwc_gadget = dwc->dwc_gadget;
int i;

if (req->queued) {
@@ -261,8 +262,8 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
if (dwc->ep0_bounced && dep->number == 0)
dwc->ep0_bounced = false;
else
- usb_gadget_unmap_request(&dwc->gadget, &req->request,
- req->direction);
+ usb_gadget_unmap_request(&dwc_gadget->gadget, &req->request,
+ req->direction);

dev_dbg(dwc->dev, "request %p from %s completed %d/%d ===> %d\n",
req, dep->name, req->request.actual,
@@ -407,6 +408,7 @@ static int dwc3_gadget_set_ep_config(struct dwc3 *dwc, struct dwc3_ep *dep,
bool ignore, bool restore)
{
struct dwc3_gadget_ep_cmd_params params;
+ struct dwc3_gadget *dwc_gadget = dwc->dwc_gadget;

memset(&params, 0x00, sizeof(params));

@@ -414,7 +416,7 @@ static int dwc3_gadget_set_ep_config(struct dwc3 *dwc, struct dwc3_ep *dep,
| DWC3_DEPCFG_MAX_PACKET_SIZE(usb_endpoint_maxp(desc));

/* Burst size is only needed in SuperSpeed mode */
- if (dwc->gadget.speed == USB_SPEED_SUPER) {
+ if (dwc_gadget->gadget.speed == USB_SPEED_SUPER) {
u32 burst = dep->endpoint.maxburst - 1;

params.param0 |= DWC3_DEPCFG_BURST_SIZE(burst);
@@ -928,6 +930,7 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param,
struct dwc3_gadget_ep_cmd_params params;
struct dwc3_request *req;
struct dwc3 *dwc = dep->dwc;
+ struct dwc3_gadget *dwc_gadget = dwc->dwc_gadget;
int ret;
u32 cmd;

@@ -980,8 +983,8 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param,
* here and stop, unmap, free and del each of the linked
* requests instead of what we do now.
*/
- usb_gadget_unmap_request(&dwc->gadget, &req->request,
- req->direction);
+ usb_gadget_unmap_request(&dwc_gadget->gadget, &req->request,
+ req->direction);
list_del(&req->list);
return ret;
}
@@ -1029,6 +1032,7 @@ static void dwc3_gadget_start_isoc(struct dwc3 *dwc,
static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
{
struct dwc3 *dwc = dep->dwc;
+ struct dwc3_gadget *dwc_gadget = dwc->dwc_gadget;
int ret;

req->request.actual = 0;
@@ -1048,7 +1052,7 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
* This will also avoid Host cancelling URBs due to too
* many NAKs.
*/
- ret = usb_gadget_map_request(&dwc->gadget, &req->request,
+ ret = usb_gadget_map_request(&dwc_gadget->gadget, &req->request,
dep->direction);
if (ret)
return ret;
@@ -1320,7 +1324,8 @@ static const struct usb_ep_ops dwc3_gadget_ep_ops = {

static int dwc3_gadget_get_frame(struct usb_gadget *g)
{
- struct dwc3 *dwc = gadget_to_dwc(g);
+ struct dwc3_gadget *dwc_gadget = gadget_to_dwc_gadget(g);
+ struct dwc3 *dwc = dwc_gadget->dwc;
u32 reg;

reg = dwc3_readl(dwc->regs, DWC3_DSTS);
@@ -1329,7 +1334,8 @@ static int dwc3_gadget_get_frame(struct usb_gadget *g)

static int dwc3_gadget_wakeup(struct usb_gadget *g)
{
- struct dwc3 *dwc = gadget_to_dwc(g);
+ struct dwc3_gadget *dwc_gadget = gadget_to_dwc_gadget(g);
+ struct dwc3 *dwc = dwc_gadget->dwc;

unsigned long timeout;
unsigned long flags;
@@ -1410,7 +1416,8 @@ out:
static int dwc3_gadget_set_selfpowered(struct usb_gadget *g,
int is_selfpowered)
{
- struct dwc3 *dwc = gadget_to_dwc(g);
+ struct dwc3_gadget *dwc_gadget = gadget_to_dwc_gadget(g);
+ struct dwc3 *dwc = dwc_gadget->dwc;
unsigned long flags;

spin_lock_irqsave(&dwc->lock, flags);
@@ -1476,7 +1483,8 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend)

static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
{
- struct dwc3 *dwc = gadget_to_dwc(g);
+ struct dwc3_gadget *dwc_gadget = gadget_to_dwc_gadget(g);
+ struct dwc3 *dwc = dwc_gadget->dwc;
unsigned long flags;
int ret;

@@ -1519,7 +1527,8 @@ static irqreturn_t dwc3_thread_interrupt(int irq, void *_dwc);
static int dwc3_gadget_start(struct usb_gadget *g,
struct usb_gadget_driver *driver)
{
- struct dwc3 *dwc = gadget_to_dwc(g);
+ struct dwc3_gadget *dwc_gadget = gadget_to_dwc_gadget(g);
+ struct dwc3 *dwc = dwc_gadget->dwc;
struct dwc3_ep *dep;
unsigned long flags;
int ret = 0;
@@ -1539,7 +1548,7 @@ static int dwc3_gadget_start(struct usb_gadget *g,

if (dwc->gadget_driver) {
dev_err(dwc->dev, "%s is already bound to %s\n",
- dwc->gadget.name,
+ dwc_gadget->gadget.name,
dwc->gadget_driver->driver.name);
ret = -EBUSY;
goto err1;
@@ -1632,7 +1641,8 @@ err0:

static int dwc3_gadget_stop(struct usb_gadget *g)
{
- struct dwc3 *dwc = gadget_to_dwc(g);
+ struct dwc3_gadget *dwc_gadget = gadget_to_dwc_gadget(g);
+ struct dwc3 *dwc = dwc_gadget->dwc;
unsigned long flags;
int irq;

@@ -1667,6 +1677,7 @@ static int dwc3_gadget_init_hw_endpoints(struct dwc3 *dwc,
u8 num, u32 direction)
{
struct dwc3_ep *dep;
+ struct dwc3_gadget *dwc_gadget = dwc->dwc_gadget;
u8 i;

for (i = 0; i < num; i++) {
@@ -1693,7 +1704,7 @@ static int dwc3_gadget_init_hw_endpoints(struct dwc3 *dwc,
dep->endpoint.maxburst = 1;
dep->endpoint.ops = &dwc3_gadget_ep0_ops;
if (!epnum)
- dwc->gadget.ep0 = &dep->endpoint;
+ dwc_gadget->gadget.ep0 = &dep->endpoint;
} else {
int ret;

@@ -1701,7 +1712,7 @@ static int dwc3_gadget_init_hw_endpoints(struct dwc3 *dwc,
dep->endpoint.max_streams = 15;
dep->endpoint.ops = &dwc3_gadget_ep_ops;
list_add_tail(&dep->endpoint.ep_list,
- &dwc->gadget.ep_list);
+ &dwc_gadget->gadget.ep_list);

ret = dwc3_alloc_trb_pool(dep);
if (ret)
@@ -1718,8 +1729,9 @@ static int dwc3_gadget_init_hw_endpoints(struct dwc3 *dwc,
static int dwc3_gadget_init_endpoints(struct dwc3 *dwc)
{
int ret;
+ struct dwc3_gadget *dwc_gadget = dwc->dwc_gadget;

- INIT_LIST_HEAD(&dwc->gadget.ep_list);
+ INIT_LIST_HEAD(&dwc_gadget->gadget.ep_list);

ret = dwc3_gadget_init_hw_endpoints(dwc, dwc->num_out_eps, 0);
if (ret < 0) {
@@ -2020,38 +2032,46 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,

static void dwc3_disconnect_gadget(struct dwc3 *dwc)
{
+ struct dwc3_gadget *dwc_gadget = dwc->dwc_gadget;
+
if (dwc->gadget_driver && dwc->gadget_driver->disconnect) {
spin_unlock(&dwc->lock);
- dwc->gadget_driver->disconnect(&dwc->gadget);
+ dwc->gadget_driver->disconnect(&dwc_gadget->gadget);
spin_lock(&dwc->lock);
}
}

static void dwc3_suspend_gadget(struct dwc3 *dwc)
{
+ struct dwc3_gadget *dwc_gadget = dwc->dwc_gadget;
+
if (dwc->gadget_driver && dwc->gadget_driver->suspend) {
spin_unlock(&dwc->lock);
- dwc->gadget_driver->suspend(&dwc->gadget);
+ dwc->gadget_driver->suspend(&dwc_gadget->gadget);
spin_lock(&dwc->lock);
}
}

static void dwc3_resume_gadget(struct dwc3 *dwc)
{
+ struct dwc3_gadget *dwc_gadget = dwc->dwc_gadget;
+
if (dwc->gadget_driver && dwc->gadget_driver->resume) {
spin_unlock(&dwc->lock);
- dwc->gadget_driver->resume(&dwc->gadget);
+ dwc->gadget_driver->resume(&dwc_gadget->gadget);
}
}

static void dwc3_reset_gadget(struct dwc3 *dwc)
{
+ struct dwc3_gadget *dwc_gadget = dwc->dwc_gadget;
+
if (!dwc->gadget_driver)
return;

- if (dwc->gadget.speed != USB_SPEED_UNKNOWN) {
+ if (dwc_gadget->gadget.speed != USB_SPEED_UNKNOWN) {
spin_unlock(&dwc->lock);
- usb_gadget_udc_reset(&dwc->gadget, dwc->gadget_driver);
+ usb_gadget_udc_reset(&dwc_gadget->gadget, dwc->gadget_driver);
spin_lock(&dwc->lock);
}
}
@@ -2145,6 +2165,7 @@ static void dwc3_clear_stall_all_ep(struct dwc3 *dwc)
static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc)
{
int reg;
+ struct dwc3_gadget *dwc_gadget = dwc->dwc_gadget;

reg = dwc3_readl(dwc->regs, DWC3_DCTL);
reg &= ~DWC3_DCTL_INITU1ENA;
@@ -2156,9 +2177,9 @@ static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc)
dwc3_disconnect_gadget(dwc);
dwc->start_config_issued = false;

- dwc->gadget.speed = USB_SPEED_UNKNOWN;
+ dwc_gadget->gadget.speed = USB_SPEED_UNKNOWN;
dwc->setup_packet_pending = false;
- usb_gadget_set_state(&dwc->gadget, USB_STATE_NOTATTACHED);
+ usb_gadget_set_state(&dwc_gadget->gadget, USB_STATE_NOTATTACHED);
}

static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
@@ -2241,6 +2262,7 @@ static void dwc3_update_ram_clk_sel(struct dwc3 *dwc, u32 speed)
static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
{
struct dwc3_ep *dep;
+ struct dwc3_gadget *dwc_gadget = dwc->dwc_gadget;
int ret;
u32 reg;
u8 speed;
@@ -2270,24 +2292,24 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
dwc3_gadget_reset_interrupt(dwc);

dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512);
- dwc->gadget.ep0->maxpacket = 512;
- dwc->gadget.speed = USB_SPEED_SUPER;
+ dwc_gadget->gadget.ep0->maxpacket = 512;
+ dwc_gadget->gadget.speed = USB_SPEED_SUPER;
break;
case DWC3_DCFG_HIGHSPEED:
dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(64);
- dwc->gadget.ep0->maxpacket = 64;
- dwc->gadget.speed = USB_SPEED_HIGH;
+ dwc_gadget->gadget.ep0->maxpacket = 64;
+ dwc_gadget->gadget.speed = USB_SPEED_HIGH;
break;
case DWC3_DCFG_FULLSPEED2:
case DWC3_DCFG_FULLSPEED1:
dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(64);
- dwc->gadget.ep0->maxpacket = 64;
- dwc->gadget.speed = USB_SPEED_FULL;
+ dwc_gadget->gadget.ep0->maxpacket = 64;
+ dwc_gadget->gadget.speed = USB_SPEED_FULL;
break;
case DWC3_DCFG_LOWSPEED:
dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(8);
- dwc->gadget.ep0->maxpacket = 8;
- dwc->gadget.speed = USB_SPEED_LOW;
+ dwc_gadget->gadget.ep0->maxpacket = 8;
+ dwc_gadget->gadget.speed = USB_SPEED_LOW;
break;
}

@@ -2351,12 +2373,13 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)

static void dwc3_gadget_wakeup_interrupt(struct dwc3 *dwc)
{
+ struct dwc3_gadget *dwc_gadget = dwc->dwc_gadget;
/*
* TODO take core out of low power mode when that's
* implemented.
*/

- dwc->gadget_driver->resume(&dwc->gadget);
+ dwc->gadget_driver->resume(&dwc_gadget->gadget);
}

static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc,
@@ -2667,6 +2690,7 @@ static irqreturn_t dwc3_interrupt(int irq, void *_dwc)
int dwc3_gadget_init(struct dwc3 *dwc)
{
int ret;
+ struct dwc3_gadget *dwc_gadget;

dwc->ctrl_req = dma_alloc_coherent(dwc->dev, sizeof(*dwc->ctrl_req),
&dwc->ctrl_req_addr, GFP_KERNEL);
@@ -2699,17 +2723,24 @@ int dwc3_gadget_init(struct dwc3 *dwc)
goto err3;
}

- dwc->gadget.ops = &dwc3_gadget_ops;
- dwc->gadget.max_speed = USB_SPEED_SUPER;
- dwc->gadget.speed = USB_SPEED_UNKNOWN;
- dwc->gadget.sg_supported = true;
- dwc->gadget.name = "dwc3-gadget";
+ dwc_gadget = kzalloc(sizeof(*dwc_gadget), GFP_KERNEL);
+ if (!dwc_gadget) {
+ ret = -ENOMEM;
+ goto err3;
+ }
+
+ dwc_gadget->gadget.ops = &dwc3_gadget_ops;
+ dwc_gadget->gadget.max_speed = USB_SPEED_SUPER;
+ dwc_gadget->gadget.speed = USB_SPEED_UNKNOWN;
+ dwc_gadget->gadget.sg_supported = true;
+ dwc_gadget->gadget.name = "dwc3-gadget";
+ dwc_gadget->dwc = dwc;

/*
* Per databook, DWC3 needs buffer size to be aligned to MaxPacketSize
* on ep out.
*/
- dwc->gadget.quirk_ep_out_aligned_size = true;
+ dwc_gadget->gadget.quirk_ep_out_aligned_size = true;

/*
* REVISIT: Here we should clear all pending IRQs to be
@@ -2720,7 +2751,7 @@ int dwc3_gadget_init(struct dwc3 *dwc)
if (ret)
goto err4;

- ret = usb_add_gadget_udc(dwc->dev, &dwc->gadget);
+ ret = usb_add_gadget_udc(dwc->dev, &dwc_gadget->gadget);
if (ret) {
dev_err(dwc->dev, "failed to register udc\n");
goto err4;
@@ -2752,7 +2783,9 @@ err0:

void dwc3_gadget_exit(struct dwc3 *dwc)
{
- usb_del_gadget_udc(&dwc->gadget);
+ struct dwc3_gadget *dwc_gadget = dwc->dwc_gadget;
+
+ usb_del_gadget_udc(&dwc_gadget->gadget);

dwc3_gadget_free_endpoints(dwc);

diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h
index 18ae3ea..b6c3e40 100644
--- a/drivers/usb/dwc3/gadget.h
+++ b/drivers/usb/dwc3/gadget.h
@@ -26,6 +26,7 @@
struct dwc3;
#define to_dwc3_ep(ep) (container_of(ep, struct dwc3_ep, endpoint))
#define gadget_to_dwc(g) (container_of(g, struct dwc3, gadget))
+#define gadget_to_dwc_gadget(g) (container_of(g, struct dwc3_gadget, gadget))

/* DEPCFG parameter 1 */
#define DWC3_DEPCFG_INT_NUM(n) ((n) << 0)
--
1.8.3.1

2014-11-25 13:16:52

by George Cherian

[permalink] [raw]
Subject: [PATCH 16/19] arm: dts: omap5: Add named interrupt property for dwc3

Add interrupt names so that the same can be used for OTG easily.

Signed-off-by: George Cherian <[email protected]>
---
arch/arm/boot/dts/omap5.dtsi | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/arch/arm/boot/dts/omap5.dtsi b/arch/arm/boot/dts/omap5.dtsi
index 256b7f6..a712ad9 100644
--- a/arch/arm/boot/dts/omap5.dtsi
+++ b/arch/arm/boot/dts/omap5.dtsi
@@ -817,7 +817,12 @@
dwc3@4a030000 {
compatible = "snps,dwc3";
reg = <0x4a030000 0x10000>;
- interrupts = <GIC_SPI 92 IRQ_TYPE_LEVEL_HIGH>;
+ interrupts = <GIC_SPI 92 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 92 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 93 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "peripheral",
+ "host",
+ "otg";
phys = <&usb2_phy>, <&usb3_phy>;
phy-names = "usb2-phy", "usb3-phy";
dr_mode = "peripheral";
--
1.8.3.1

2014-11-25 13:16:58

by George Cherian

[permalink] [raw]
Subject: [PATCH 15/19] arm: dts: am4372: Add named interrupt property for dwc3

From: Felipe Balbi <[email protected]>

Add interrupt names so that the same can be used for OTG easily.

Signed-off-by: Felipe Balbi <[email protected]>
Signed-off-by: George Cherian <[email protected]>
---
arch/arm/boot/dts/am4372.dtsi | 18 ++++++++++++++----
1 file changed, 14 insertions(+), 4 deletions(-)

diff --git a/arch/arm/boot/dts/am4372.dtsi b/arch/arm/boot/dts/am4372.dtsi
index e19068d..3dda4c8 100644
--- a/arch/arm/boot/dts/am4372.dtsi
+++ b/arch/arm/boot/dts/am4372.dtsi
@@ -810,8 +810,13 @@

usb1: usb@48390000 {
compatible = "synopsys,dwc3";
- reg = <0x48390000 0x10000>;
- interrupts = <GIC_SPI 168 IRQ_TYPE_LEVEL_HIGH>;
+ reg = <0x48390000 0x17000>;
+ interrupts = <GIC_SPI 168 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 168 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 172 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "peripheral",
+ "host",
+ "otg";
phys = <&usb2_phy1>;
phy-names = "usb2-phy";
maximum-speed = "high-speed";
@@ -834,8 +839,13 @@

usb2: usb@483d0000 {
compatible = "synopsys,dwc3";
- reg = <0x483d0000 0x10000>;
- interrupts = <GIC_SPI 174 IRQ_TYPE_LEVEL_HIGH>;
+ reg = <0x483d0000 0x17000>;
+ interrupts = <GIC_SPI 174 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 174 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 178 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "peripheral",
+ "host",
+ "otg";
phys = <&usb2_phy2>;
phy-names = "usb2-phy";
maximum-speed = "high-speed";
--
1.8.3.1

2014-11-25 13:17:04

by George Cherian

[permalink] [raw]
Subject: [PATCH 18/19] arm: dts: exynos5250: Add named interrupt property for dwc3

Add interrupt names so that the same can be used for OTG easily.

Signed-off-by: George Cherian <[email protected]>
---
arch/arm/boot/dts/exynos5250.dtsi | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/arch/arm/boot/dts/exynos5250.dtsi b/arch/arm/boot/dts/exynos5250.dtsi
index f21b9aa..c74cd8b 100644
--- a/arch/arm/boot/dts/exynos5250.dtsi
+++ b/arch/arm/boot/dts/exynos5250.dtsi
@@ -558,7 +558,12 @@
dwc3 {
compatible = "synopsys,dwc3";
reg = <0x12000000 0x10000>;
- interrupts = <0 72 0>;
+ interrupts = <0 72 0>,
+ <0 72 0>,
+ <0 72 0>;
+ interrupt-names = "peripheral",
+ "host",
+ "otg";
phys = <&usbdrd_phy 0>, <&usbdrd_phy 1>;
phy-names = "usb2-phy", "usb3-phy";
};
--
1.8.3.1

2014-11-25 13:17:15

by George Cherian

[permalink] [raw]
Subject: [PATCH 19/19] arm: dts: am43x evms: Make usb1 as OTG

USB1 of am43x EPOS EVM, am437x GP EVM and am437x SK EVM can be used as OTG.
Enable the same.

Signed-off-by: George Cherian <[email protected]>
---
arch/arm/boot/dts/am437x-gp-evm.dts | 2 +-
arch/arm/boot/dts/am437x-sk-evm.dts | 2 +-
arch/arm/boot/dts/am43x-epos-evm.dts | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/arch/arm/boot/dts/am437x-gp-evm.dts b/arch/arm/boot/dts/am437x-gp-evm.dts
index e7ac47f..a8b5752 100644
--- a/arch/arm/boot/dts/am437x-gp-evm.dts
+++ b/arch/arm/boot/dts/am437x-gp-evm.dts
@@ -380,7 +380,7 @@
};

&usb1 {
- dr_mode = "peripheral";
+ dr_mode = "otg";
status = "okay";
};

diff --git a/arch/arm/boot/dts/am437x-sk-evm.dts b/arch/arm/boot/dts/am437x-sk-evm.dts
index 859ff3d..b258826 100644
--- a/arch/arm/boot/dts/am437x-sk-evm.dts
+++ b/arch/arm/boot/dts/am437x-sk-evm.dts
@@ -478,7 +478,7 @@
};

&usb1 {
- dr_mode = "peripheral";
+ dr_mode = "otg";
status = "okay";
};

diff --git a/arch/arm/boot/dts/am43x-epos-evm.dts b/arch/arm/boot/dts/am43x-epos-evm.dts
index ac3e485..3d106e9 100644
--- a/arch/arm/boot/dts/am43x-epos-evm.dts
+++ b/arch/arm/boot/dts/am43x-epos-evm.dts
@@ -542,7 +542,7 @@
};

&usb1 {
- dr_mode = "peripheral";
+ dr_mode = "otg";
status = "okay";
};

--
1.8.3.1

2014-11-25 13:17:13

by George Cherian

[permalink] [raw]
Subject: [PATCH 17/19] arm: dts: dra7: Add named interrupt property for dwc3

Add interrupt names so that the same can be used for OTG easily.

Signed-off-by: George Cherian <[email protected]>
---
arch/arm/boot/dts/dra7.dtsi | 28 ++++++++++++++++++++++++----
1 file changed, 24 insertions(+), 4 deletions(-)

diff --git a/arch/arm/boot/dts/dra7.dtsi b/arch/arm/boot/dts/dra7.dtsi
index 9cc9843..78f1761 100644
--- a/arch/arm/boot/dts/dra7.dtsi
+++ b/arch/arm/boot/dts/dra7.dtsi
@@ -1153,7 +1153,12 @@
usb1: usb@48890000 {
compatible = "snps,dwc3";
reg = <0x48890000 0x17000>;
- interrupts = <GIC_SPI 71 IRQ_TYPE_LEVEL_HIGH>;
+ interrupts = <GIC_SPI 71 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 71 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 72 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "peripheral",
+ "host",
+ "otg";
phys = <&usb2_phy1>, <&usb3_phy1>;
phy-names = "usb2-phy", "usb3-phy";
tx-fifo-resize;
@@ -1174,7 +1179,12 @@
usb2: usb@488d0000 {
compatible = "snps,dwc3";
reg = <0x488d0000 0x17000>;
- interrupts = <GIC_SPI 73 IRQ_TYPE_LEVEL_HIGH>;
+ interrupts = <GIC_SPI 73 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 73 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 87 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "peripheral",
+ "host",
+ "otg";
phys = <&usb2_phy2>;
phy-names = "usb2-phy";
tx-fifo-resize;
@@ -1197,7 +1207,12 @@
usb3: usb@48910000 {
compatible = "snps,dwc3";
reg = <0x48910000 0x17000>;
- interrupts = <GIC_SPI 88 IRQ_TYPE_LEVEL_HIGH>;
+ interrupts = <GIC_SPI 88 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 88 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 344 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "peripheral",
+ "host",
+ "otg";
tx-fifo-resize;
maximum-speed = "high-speed";
dr_mode = "otg";
@@ -1217,7 +1232,12 @@
usb4: usb@48950000 {
compatible = "snps,dwc3";
reg = <0x48950000 0x17000>;
- interrupts = <GIC_SPI 345 IRQ_TYPE_LEVEL_HIGH>;
+ interrupts = <GIC_SPI 345 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 345 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 346 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "peripheral",
+ "host",
+ "otg";
tx-fifo-resize;
maximum-speed = "high-speed";
dr_mode = "otg";
--
1.8.3.1

2014-11-25 13:16:49

by George Cherian

[permalink] [raw]
Subject: [PATCH 14/19] usb: dwc3: otg: Add the initial otg driver for dwc3.

Add the Initial OTG driver for dwc3.
Currently support only
* ID based Role switching.

Signed-off-by: George Cherian <[email protected]>
---
drivers/usb/dwc3/Makefile | 4 ++
drivers/usb/dwc3/core.c | 10 +---
drivers/usb/dwc3/core.h | 10 ++++
drivers/usb/dwc3/otg.c | 126 ++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 142 insertions(+), 8 deletions(-)
create mode 100644 drivers/usb/dwc3/otg.c

diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile
index bb34fbc..fe7af97 100644
--- a/drivers/usb/dwc3/Makefile
+++ b/drivers/usb/dwc3/Makefile
@@ -12,6 +12,10 @@ ifneq ($(filter y,$(CONFIG_USB_DWC3_HOST) $(CONFIG_USB_DWC3_DUAL_ROLE)),)
dwc3-y += host.o
endif

+ifneq ($(CONFIG_USB_DWC3_DUAL_ROLE),)
+ dwc3-y += otg.o
+endif
+
ifneq ($(filter y,$(CONFIG_USB_DWC3_GADGET) $(CONFIG_USB_DWC3_DUAL_ROLE)),)
dwc3-y += gadget.o ep0.o
endif
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index dbd5589..dd4af3f 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -685,15 +685,9 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
break;
case USB_DR_MODE_OTG:
dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_OTG);
- ret = dwc3_host_init(dwc);
- if (ret) {
- dev_err(dev, "failed to initialize host\n");
- return ret;
- }
-
- ret = dwc3_gadget_init(dwc);
+ ret = dwc3_otg_init(dwc);
if (ret) {
- dev_err(dev, "failed to initialize gadget\n");
+ dev_err(dev, "failed to initialize otg\n");
return ret;
}
break;
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index eb2e970..001d77d 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -1103,6 +1103,16 @@ static inline int dwc3_send_gadget_generic_command(struct dwc3 *dwc,
{ return 0; }
#endif

+#if IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)
+int dwc3_otg_init(struct dwc3 *dwc);
+void dwc3_otg_exit(struct dwc3 *dwc);
+#else
+static inline int dwc3_otg_init(struct dwc3 *dwc)
+{ return 0; }
+static inline void dwc3_otg_exit(struct dwc3 *dwc)
+{ }
+#endif
+
/* power management interface */
#if !IS_ENABLED(CONFIG_USB_DWC3_HOST)
int dwc3_gadget_suspend(struct dwc3 *dwc);
diff --git a/drivers/usb/dwc3/otg.c b/drivers/usb/dwc3/otg.c
new file mode 100644
index 0000000..b5c31c0
--- /dev/null
+++ b/drivers/usb/dwc3/otg.c
@@ -0,0 +1,126 @@
+/**
+ * otg.c - DesignWare USB3 DRD Controller OTG
+ *
+ * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com
+ *
+ * Authors: George Cherian <[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 of
+ * the License 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/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+
+#include <linux/usb/drd.h>
+#include "core.h"
+#include "io.h"
+
+#define DWC3_GSTS_OTG_IP (1 << 10)
+
+static irqreturn_t dwc3_otg_interrupt(int irq , void *_dwc)
+{
+ struct dwc3 *dwc = _dwc;
+ u32 reg;
+
+ spin_lock(&dwc->lock);
+ reg = dwc3_readl(dwc->regs, DWC3_GSTS);
+ if (reg & DWC3_GSTS_OTG_IP) {
+ reg = dwc3_readl(dwc->regs, DWC3_OEVT);
+ dev_vdbg(dwc->dev, "OTG Interrupt %x\n", reg);
+ dwc3_writel(dwc->regs, DWC3_OEVT, reg);
+ spin_unlock(&dwc->lock);
+ return IRQ_WAKE_THREAD;
+ }
+
+ spin_unlock(&dwc->lock);
+ return IRQ_NONE;
+}
+
+static irqreturn_t dwc3_otg_thread_interrupt(int irq, void *_dwc)
+{
+ struct dwc3 *dwc = _dwc;
+ u32 reg = dwc3_readl(dwc->regs, DWC3_OSTS);
+
+ dev_vdbg(dwc->dev, "OTG thread interrupt\n");
+ if ((reg & DWC3_OSTS_CONIDSTS)) {
+ usb_drd_stop_hcd(dwc->dev);
+ dwc3_writel(dwc->regs, DWC3_OCFG, DWC3_OCFG_SFTRSTMASK);
+ dwc3_writel(dwc->regs, DWC3_OCTL,
+ DWC3_OCTL_SESREQ | DWC3_OCTL_PERIMODE);
+ if (usb_drd_get_state(dwc->dev) & DRD_DEVICE_REGISTERED) {
+ usb_drd_start_udc(dwc->dev);
+ } else {
+ dwc3_core_gadget_helper(dwc);
+ dwc3_gadget_init(dwc);
+ }
+ dwc3_writel(dwc->regs, DWC3_OEVTEN,
+ DWC3_OEVTEN_CONIDSTSCHNGEN);
+ } else if (!(reg & DWC3_OSTS_CONIDSTS)) {
+ usb_drd_stop_udc(dwc->dev);
+ dwc3_writel(dwc->regs, DWC3_OCFG,
+ DWC3_OCFG_DISPWRCUTTOFF | DWC3_OCFG_SFTRSTMASK);
+ dwc3_writel(dwc->regs, DWC3_OCTL, DWC3_OCTL_PRTPWRCTL);
+ if (usb_drd_get_state(dwc->dev) & DRD_HOST_REGISTERED)
+ usb_drd_start_hcd(dwc->dev);
+ else
+ dwc3_host_init(dwc);
+
+ dwc3_writel(dwc->regs, DWC3_OEVTEN,
+ DWC3_OEVTEN_CONIDSTSCHNGEN);
+ }
+
+ return IRQ_HANDLED;
+}
+
+int dwc3_otg_init(struct dwc3 *dwc)
+{
+ u32 reg, ret;
+
+ usb_drd_add(dwc->dev);
+ dwc3_writel(dwc->regs, DWC3_OEVT, 0xFFFF);
+ if (dwc->otg_irq > 0) {
+ ret = devm_request_threaded_irq(dwc->dev, dwc->otg_irq,
+ dwc3_otg_interrupt,
+ dwc3_otg_thread_interrupt,
+ IRQF_SHARED, "dwc3-otg", dwc);
+ } else {
+ WARN(1, "Trying to request invalid otg_irq");
+ return -ENODEV;
+ }
+
+ dwc3_writel(dwc->regs, DWC3_OEVTEN, DWC3_OEVTEN_CONIDSTSCHNGEN);
+ dwc3_writel(dwc->regs, DWC3_OCTL, DWC3_OCTL_PERIMODE);
+
+ reg = dwc3_readl(dwc->regs, DWC3_OSTS);
+ if ((reg & DWC3_OSTS_CONIDSTS)) {
+ dev_vdbg(dwc->dev, "Gadget init\n");
+ dwc3_writel(dwc->regs, DWC3_OCFG, DWC3_OCFG_SFTRSTMASK);
+ dwc3_writel(dwc->regs, DWC3_OCTL,
+ DWC3_OCTL_SESREQ | DWC3_OCTL_PERIMODE);
+ dwc3_gadget_init(dwc);
+ dwc3_writel(dwc->regs, DWC3_OEVTEN,
+ DWC3_OEVTEN_CONIDSTSCHNGEN);
+
+ } else if (!(reg & DWC3_OSTS_CONIDSTS)) {
+ dev_vdbg(dwc->dev, "Host init\n");
+ dwc3_writel(dwc->regs, DWC3_OCFG,
+ DWC3_OCFG_DISPWRCUTTOFF | DWC3_OCFG_SFTRSTMASK);
+ dwc3_writel(dwc->regs, DWC3_OCTL, DWC3_OCTL_PRTPWRCTL);
+ dwc3_host_init(dwc);
+ dwc3_writel(dwc->regs, DWC3_OEVTEN,
+ DWC3_OEVTEN_CONIDSTSCHNGEN);
+ }
+
+ return 0;
+}
--
1.8.3.1

2014-11-25 13:19:20

by George Cherian

[permalink] [raw]
Subject: [PATCH 13/19] usb: dwc3: core: Add DWC3 OTG specific register defines

Add OTG reggister defines to DWC3 core.h

Signed-off-by: George Cherian <[email protected]>
---
drivers/usb/dwc3/core.h | 68 +++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 68 insertions(+)

diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 7fbe736..eb2e970 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -381,6 +381,74 @@
#define DWC3_DEPCMD_TYPE_BULK 2
#define DWC3_DEPCMD_TYPE_INTR 3

+/* OTG Configuration Register */
+#define DWC3_OCFG_DISPWRCUTTOFF (1 << 5)
+#define DWC3_OCFG_HIBDISMASK (1 << 4)
+#define DWC3_OCFG_SFTRSTMASK (1 << 3)
+#define DWC3_OCFG_OTGVERSION (1 << 2)
+#define DWC3_OCFG_HNPCAP (1 << 1)
+#define DWC3_OCFG_SRPCAP (1 << 0)
+
+/* OTG CTL Register */
+#define DWC3_OCTL_OTG3GOERR (1 << 7)
+#define DWC3_OCTL_PERIMODE (1 << 6)
+#define DWC3_OCTL_PRTPWRCTL (1 << 5)
+#define DWC3_OCTL_HNPREQ (1 << 4)
+#define DWC3_OCTL_SESREQ (1 << 3)
+#define DWC3_OCTL_TERMSELIDPULSE (1 << 2)
+#define DWC3_OCTL_DEVSETHNPEN (1 << 1)
+#define DWC3_OCTL_HOSTSETHNPEN (1 << 0)
+
+/* OTG Event Register */
+#define DWC3_OEVT_DEVICEMODE (1 << 31)
+#define DWC3_OEVT_XHCIRUNSTPSET (1 << 27)
+#define DWC3_OEVT_DEVRUNSTPSET (1 << 26)
+#define DWC3_OEVT_HIBENTRY (1 << 25)
+#define DWC3_OEVT_IDSTSCHNG (1 << 24)
+#define DWC3_OEVT_HRRCONFNOTIF (1 << 23)
+#define DWC3_OEVT_HRRINITNOTIF (1 << 22)
+#define DWC3_OEVT_ADEVIDLE (1 << 21)
+#define DWC3_OEVT_ADEVBHOSTEND (1 << 20)
+#define DWC3_OEVT_ADEVHOST (1 << 19)
+#define DWC3_OEVT_ADEVHNPCHNG (1 << 18)
+#define DWC3_OEVT_ADEVSRPDET (1 << 17)
+#define DWC3_OEVT_ADEVSESSENDDET (1 << 16)
+#define DWC3_OEVT_BDEVBHOSTEND (1 << 11)
+#define DWC3_OEVT_BDEVHNPCHNG (1 << 10)
+#define DWC3_OEVT_BDEVSESSVLDDET (1 << 9)
+#define DWC3_OEVT_BDEVVBUSCHNG (1 << 8)
+#define DWC3_OEVT_BSESSVLD (1 << 3)
+#define DWC3_OEVT_HOSTNEGSTS (1 << 2)
+#define DWC3_OEVT_SESSREQSTS (1 << 1)
+#define DWC3_OEVT_ERR (1 << 0)
+
+/* OTG Event Enable Register */
+#define DWC3_OEVTEN_XHCIRUNSTPSETEN (1 << 27)
+#define DWC3_OEVTEN_DEVRUNSTPSETEN (1 << 26)
+#define DWC3_OEVTEN_HIBENTRYEN (1 << 25)
+#define DWC3_OEVTEN_CONIDSTSCHNGEN (1 << 24)
+#define DWC3_OEVTEN_HRRCONFNOTIFEN (1 << 23)
+#define DWC3_OEVTEN_HRRINITNOTIFEN (1 << 22)
+#define DWC3_OEVTEN_ADEVIDLEEN (1 << 21)
+#define DWC3_OEVTEN_ADEVBHOSTENDEN (1 << 20)
+#define DWC3_OEVTEN_ADEVHOSTEN (1 << 19)
+#define DWC3_OEVTEN_ADEVHNPCHNGEN (1 << 18)
+#define DWC3_OEVTEN_ADEVSRPDETEN (1 << 17)
+#define DWC3_OEVTEN_ADEVSESSENDDETEN (1 << 16)
+#define DWC3_OEVTEN_BDEVHOSTENDEN (1 << 11)
+#define DWC3_OEVTEN_BDEVHNPCHNGEN (1 << 10)
+#define DWC3_OEVTEN_BDEVSESSVLDDETEN (1 << 9)
+#define DWC3_OEVTEN_BDEVVBUSCHNGEVNTEN (1 << 8)
+
+/* OTG Status Register */
+#define DWC3_OSTS_DEVRUNSTP (1 << 13)
+#define DWC3_OSTS_XHCIRUNSTP (1 << 12)
+#define DWC3_OSTS_PERIPHERALSTATE (1 << 4)
+#define DWC3_OSTS_XHCIPORTPOWER (1 << 3)
+#define DWC3_OSTS_BSESVLD (1 << 2)
+#define DWC3_OSTS_VBUSVLD (1 << 1)
+#define DWC3_OSTS_CONIDSTS (1 << 0)
+
/* Structures */

struct dwc3_trb;
--
1.8.3.1

2014-11-25 13:20:52

by George Cherian

[permalink] [raw]
Subject: [PATCH 07/19] usb: host: xhci: Adapt xhci to use usb drd library

Adapt the xhci-plat driver to use drd library functions.
In prepration to support DRD on dwc3.

Signed-off-by: George Cherian <[email protected]>
---
drivers/usb/host/xhci-plat.c | 15 +++++++++++++++
1 file changed, 15 insertions(+)

diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c
index d8d024d..fbbbd59 100644
--- a/drivers/usb/host/xhci-plat.c
+++ b/drivers/usb/host/xhci-plat.c
@@ -17,6 +17,8 @@
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
+#include <linux/usb/otg.h>
+#include <linux/usb/drd.h>
#include <linux/usb/xhci_pdriver.h>

#include "xhci.h"
@@ -78,6 +80,7 @@ static int xhci_plat_probe(struct platform_device *pdev)
struct resource *res;
struct usb_hcd *hcd;
struct clk *clk;
+ struct usb_drd_host *drd_host;
int ret;
int irq;

@@ -169,6 +172,17 @@ static int xhci_plat_probe(struct platform_device *pdev)
if (ret)
goto put_usb3_hcd;

+ drd_host = kzalloc(sizeof(*drd_host), GFP_KERNEL);
+ if (!drd_host)
+ return -ENOMEM;
+
+ drd_host->main_hcd = xhci->main_hcd;
+ drd_host->shared_hcd = xhci->shared_hcd;
+ drd_host->hcd_irq = irq;
+ drd_host->host_setup = NULL;
+
+ usb_drd_register_hcd(pdev->dev.parent, drd_host);
+
return 0;

put_usb3_hcd:
@@ -200,6 +214,7 @@ static int xhci_plat_remove(struct platform_device *dev)
if (!IS_ERR(clk))
clk_disable_unprepare(clk);
usb_put_hcd(hcd);
+ usb_drd_unregister_hcd(dev->dev.parent);
kfree(xhci);

return 0;
--
1.8.3.1

2014-11-25 13:21:11

by George Cherian

[permalink] [raw]
Subject: [PATCH 06/19] usb: dwc3: host: Pass the XHCI_DRD_SUPPORT and XHCI_NEEDS_LHC_RESET quirk

Pass the quir flag XHCI_DRD_SUPPORT from DWC3 host to xhci platform driver.
This enables xhci driver to handle deallocation's differently while in DRD mode.
Pass the quirk flag XHCI_NEEDS_LHC_RESET from DWC3 host to xhci platform
driver. This enables to do LHRESET during xhci_reset().

Signed-off-by: George Cherian <[email protected]>
---
drivers/usb/dwc3/host.c | 2 ++
1 file changed, 2 insertions(+)

diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c
index dcb8ca0..257b5b5 100644
--- a/drivers/usb/dwc3/host.c
+++ b/drivers/usb/dwc3/host.c
@@ -53,6 +53,8 @@ int dwc3_host_init(struct dwc3 *dwc)
#ifdef CONFIG_DWC3_HOST_USB3_LPM_ENABLE
pdata.usb3_lpm_capable = 1;
#endif
+ pdata.usb_drd_support = 1;
+ pdata.usb_needs_lhc_reset = 1;

ret = platform_device_add_data(xhci, &pdata, sizeof(pdata));
if (ret) {
--
1.8.3.1

2014-11-25 13:21:35

by George Cherian

[permalink] [raw]
Subject: [PATCH 05/19] usb: host: xhci-plat: Add support to pass XHCI_NEEDS_LHC_RESET quirk

Extend the platform data to pass XHCI_NEEDS_LHC_RESET quirk to
the xhci driver.

Signed-off-by: George Cherian <[email protected]>
---
drivers/usb/host/xhci-plat.c | 3 +++
include/linux/usb/xhci_pdriver.h | 1 +
2 files changed, 4 insertions(+)

diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c
index 2c42273..d8d024d 100644
--- a/drivers/usb/host/xhci-plat.c
+++ b/drivers/usb/host/xhci-plat.c
@@ -31,6 +31,9 @@ static void xhci_plat_quirks(struct device *dev, struct xhci_hcd *xhci)

if (pdata->usb_drd_support)
xhci->quirks |= XHCI_DRD_SUPPORT;
+
+ if (pdata->usb_needs_lhc_reset)
+ xhci->quirks |= XHCI_NEEDS_LHC_RESET;
/*
* As of now platform drivers don't provide MSI support so we ensure
* here that the generic code does not try to make a pci_dev from our
diff --git a/include/linux/usb/xhci_pdriver.h b/include/linux/usb/xhci_pdriver.h
index 539c2d8..8ef7321 100644
--- a/include/linux/usb/xhci_pdriver.h
+++ b/include/linux/usb/xhci_pdriver.h
@@ -23,6 +23,7 @@
struct usb_xhci_pdata {
unsigned usb3_lpm_capable:1;
unsigned usb_drd_support:1;
+ unsigned usb_needs_lhc_reset:1;
};

#endif /* __USB_CORE_XHCI_PDRIVER_H */
--
1.8.3.1

2014-11-25 13:21:55

by George Cherian

[permalink] [raw]
Subject: [PATCH 01/19] usb: common: drd-lib: Add DRD lib for USB.

Add USB DRD library. This Library facilitates to
switch roles between HOST and Device modes.

A DRD should be added to the library using usb_drd_add().
Register the HOST and UDC using usb_drd_register_hcd/udc().
Un-Register the HOST and UDC using usb_drd_unregister_hcd/udc().

Depending on the state of IP -
Call the following to start/stop HOST controller
usb_drd_start/stop_hcd().
This internally calls usb_add/remove_hcd() or IP specific low level start/stop
defined in ll_start/stop

Call the following to start/stop UDC
usb_drd_start/stop_udc().
This internally calls udc_start/udc_stop() or IP specific low level start/stop
defined in ll_start/stop

Signed-off-by: George Cherian <[email protected]>
---
drivers/usb/Kconfig | 15 ++
drivers/usb/common/Makefile | 1 +
drivers/usb/common/drd-lib.c | 346 +++++++++++++++++++++++++++++++++++++++++++
include/linux/usb/drd.h | 77 ++++++++++
4 files changed, 439 insertions(+)
create mode 100644 drivers/usb/common/drd-lib.c
create mode 100644 include/linux/usb/drd.h

diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index ae481c3..ea0d944 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -34,6 +34,21 @@ config USB_COMMON
default y
depends on USB || USB_GADGET

+config DRD_LIB
+ tristate "DRD Library support"
+ default y
+ depends on USB && USB_GADGET
+ ---help---
+ This option adds DRD Library support for Universal Serial Bus (USB).
+ DRD Library faciliatets the Role switching by HOST and DEVICE roles,
+ If your hardware has a Dual Role Device.
+
+ The DRD Library uses USB core API's to start/stop HOST controllers,
+ UDC API's to start/stop DEVICE controllers, ther by enabling to
+ switch roles between HOST and Device modes.
+
+ Say N if unsure.
+
config USB_ARCH_HAS_HCD
def_bool y

diff --git a/drivers/usb/common/Makefile b/drivers/usb/common/Makefile
index ca2f8bd..e2c1593 100644
--- a/drivers/usb/common/Makefile
+++ b/drivers/usb/common/Makefile
@@ -7,3 +7,4 @@ usb-common-y += common.o
usb-common-$(CONFIG_USB_LED_TRIG) += led.o

obj-$(CONFIG_USB_OTG_FSM) += usb-otg-fsm.o
+obj-$(CONFIG_DRD_LIB) += drd-lib.o
diff --git a/drivers/usb/common/drd-lib.c b/drivers/usb/common/drd-lib.c
new file mode 100644
index 0000000..6159436
--- /dev/null
+++ b/drivers/usb/common/drd-lib.c
@@ -0,0 +1,346 @@
+/**
+ * drd-lib.c - USB DRD library functions
+ *
+ * Copyright (C) 2014 Texas Instruments
+ * Author: George Cherian <[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 of
+ * the License 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+
+#include <linux/usb/hcd.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/drd.h>
+
+/**
+ * struct usb_drd - describes one dual role device
+ * @host - the HOST controller device of this drd
+ * @gadget - the gadget of drd
+ * @parent - the device to the actual controller
+ * @list - for use by the drd lib
+ * @state - specifies the current state
+ *
+ * This represents the internal data structure which is used by the UDC-class
+ * to hold information about udc driver and gadget together.
+ */
+struct usb_drd {
+ struct usb_drd_host *host;
+ struct usb_drd_gadget *gadget;
+ struct device *parent;
+ struct list_head list;
+ unsigned int state;
+};
+
+static LIST_HEAD(drd_list);
+static DEFINE_SPINLOCK(drd_lock);
+
+static struct usb_drd *usb_drd_get_dev(struct device *parent)
+{
+ struct usb_drd *drd;
+
+ spin_lock(&drd_lock);
+ list_for_each_entry(drd, &drd_list, list)
+ if (drd->parent == parent)
+ goto out;
+ drd = NULL;
+out:
+ spin_unlock(&drd_lock);
+
+ return drd;
+}
+
+int usb_drd_get_state(struct device *parent)
+{
+ struct usb_drd *drd;
+
+ drd = usb_drd_get_dev(parent);
+ if (!drd)
+ return -ENODEV;
+
+ return drd->state;
+}
+EXPORT_SYMBOL_GPL(usb_drd_get_state);
+
+int usb_drd_release(struct device *parent)
+{
+ struct usb_drd *drd;
+ int ret;
+
+ spin_lock(&drd_lock);
+ list_for_each_entry(drd, &drd_list, list) {
+ if (drd->parent == parent) {
+ kfree(drd);
+ ret = 0;
+ goto out;
+ }
+ }
+ ret = -ENODEV;
+out:
+ spin_unlock(&drd_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(usb_drd_release);
+
+int usb_drd_add(struct device *parent)
+{
+ struct usb_drd *drd;
+
+ drd = kzalloc(sizeof(*drd), GFP_KERNEL);
+ if (!drd)
+ return -ENOMEM;
+
+ spin_lock(&drd_lock);
+ drd->parent = parent;
+ list_add_tail(&drd->list, &drd_list);
+ drd->state = DRD_UNREGISTERED;
+
+ spin_unlock(&drd_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usb_drd_add);
+
+int usb_drd_register_hcd(struct device *parent, struct usb_drd_host *host)
+{
+ struct usb_drd *drd;
+
+ drd = usb_drd_get_dev(parent);
+ if (!drd)
+ return -ENODEV;
+
+ spin_lock(&drd_lock);
+ drd->host = host;
+ drd->state |= DRD_HOST_REGISTERED | DRD_HOST_ACTIVE;
+ spin_unlock(&drd_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usb_drd_register_hcd);
+
+int usb_drd_unregister_hcd(struct device *parent)
+{
+ struct usb_drd *drd;
+
+ drd = usb_drd_get_dev(parent);
+ if (!drd)
+ return -ENODEV;
+
+ spin_lock(&drd_lock);
+ drd->state &= ~(DRD_HOST_REGISTERED | DRD_HOST_ACTIVE);
+ spin_unlock(&drd_lock);
+ kfree(drd->host);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usb_drd_unregister_hcd);
+
+int usb_drd_start_hcd(struct device *parent)
+{
+ struct usb_drd *drd;
+ struct usb_drd_setup *setup;
+
+ drd = usb_drd_get_dev(parent);
+ if (!drd)
+ return -ENODEV;
+
+ if (WARN_ON(!(drd->state & DRD_HOST_REGISTERED)))
+ return -EINVAL;
+
+ setup = drd->host->host_setup;
+ if (setup && setup->ll_start)
+ setup->ll_start(setup->data);
+
+ usb_add_hcd(drd->host->main_hcd,
+ drd->host->hcd_irq, IRQF_SHARED);
+ if (drd->host->shared_hcd)
+ usb_add_hcd(drd->host->shared_hcd,
+ drd->host->hcd_irq, IRQF_SHARED);
+
+ spin_lock(&drd_lock);
+ drd->state |= DRD_HOST_ACTIVE;
+ spin_unlock(&drd_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usb_drd_start_hcd);
+
+int usb_drd_stop_hcd(struct device *parent)
+{
+ struct usb_drd *drd;
+ struct usb_drd_setup *setup;
+
+ drd = usb_drd_get_dev(parent);
+ if (!drd)
+ return -ENODEV;
+
+ if (WARN_ON(!(drd->state & DRD_HOST_ACTIVE)))
+ return -EINVAL;
+
+ setup = drd->host->host_setup;
+ if (setup && setup->ll_stop)
+ setup->ll_stop(setup->data);
+ if (drd->host->shared_hcd)
+ usb_remove_hcd(drd->host->shared_hcd);
+
+ usb_remove_hcd(drd->host->main_hcd);
+
+ spin_lock(&drd_lock);
+ drd->state = drd->state & ~DRD_HOST_ACTIVE;
+ spin_unlock(&drd_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usb_drd_stop_hcd);
+
+int usb_drd_register_udc(struct device *parent, struct usb_drd_gadget *gadget)
+{
+ struct usb_drd *drd;
+
+ drd = usb_drd_get_dev(parent);
+ if (!drd)
+ return -ENODEV;
+
+ spin_lock(&drd_lock);
+ drd->gadget = gadget;
+ drd->state |= DRD_DEVICE_REGISTERED;
+ if (drd->gadget->g_driver)
+ drd->state |= DRD_DEVICE_ACTIVE;
+
+ spin_unlock(&drd_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usb_drd_register_udc);
+
+int usb_drd_register_udc_driver(struct device *parent,
+ struct usb_gadget_driver *driver)
+{
+ struct usb_drd *drd;
+
+ drd = usb_drd_get_dev(parent);
+ if (!drd)
+ return -ENODEV;
+
+ spin_lock(&drd_lock);
+ drd->gadget->g_driver = driver;
+ drd->state |= DRD_DEVICE_ACTIVE;
+ spin_unlock(&drd_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usb_drd_register_udc_driver);
+
+int usb_drd_unregister_udc(struct device *parent)
+{
+ struct usb_drd *drd;
+
+ drd = usb_drd_get_dev(parent);
+ if (!drd)
+ return -ENODEV;
+
+ spin_lock(&drd_lock);
+ drd->state &= ~(DRD_DEVICE_REGISTERED | DRD_DEVICE_ACTIVE);
+ spin_unlock(&drd_lock);
+ kfree(drd->gadget->gadget_setup);
+ kfree(drd->gadget);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usb_drd_unregister_udc);
+
+int usb_drd_unregister_udc_driver(struct device *parent)
+{
+ struct usb_drd *drd;
+ struct usb_drd_gadget *drd_gadget;
+
+ drd = usb_drd_get_dev(parent);
+ if (!drd)
+ return -ENODEV;
+ drd_gadget = drd->gadget;
+
+ spin_lock(&drd_lock);
+ drd->state &= ~DRD_DEVICE_ACTIVE;
+ drd_gadget->g_driver = NULL;
+ spin_unlock(&drd_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usb_drd_unregister_udc_driver);
+
+int usb_drd_start_udc(struct device *parent)
+{
+ struct usb_drd *drd;
+ struct usb_drd_gadget *drd_gadget;
+ struct usb_drd_setup *setup;
+
+ drd = usb_drd_get_dev(parent);
+ if (!drd)
+ return -ENODEV;
+
+ if (WARN_ON(!(drd->state & DRD_DEVICE_REGISTERED)))
+ return -EINVAL;
+
+ drd_gadget = drd->gadget;
+ setup = drd_gadget->gadget_setup;
+
+ if (setup && setup->ll_start)
+ setup->ll_start(setup->data);
+
+ usb_add_gadget_udc_release(parent, drd_gadget->gadget,
+ setup->ll_release);
+ spin_lock(&drd_lock);
+ drd->state |= DRD_DEVICE_ACTIVE;
+ spin_unlock(&drd_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usb_drd_start_udc);
+
+int usb_drd_stop_udc(struct device *parent)
+{
+ struct usb_drd *drd;
+ struct usb_drd_gadget *drd_gadget;
+ struct usb_drd_setup *setup;
+
+ drd = usb_drd_get_dev(parent);
+ if (!drd)
+ return -ENODEV;
+
+ if (WARN_ON(!(drd->state & DRD_DEVICE_REGISTERED)))
+ return -EINVAL;
+
+ drd_gadget = drd->gadget;
+ setup = drd_gadget->gadget_setup;
+ if (setup && setup->ll_stop)
+ setup->ll_stop(setup->data);
+
+ usb_del_gadget_udc(drd_gadget->gadget);
+
+ spin_lock(&drd_lock);
+ drd->state = drd->state & ~DRD_DEVICE_ACTIVE;
+ spin_unlock(&drd_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usb_drd_stop_udc);
+
+MODULE_DESCRIPTION("USB-DRD Library");
+MODULE_AUTHOR("George Cherian <[email protected]>");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/usb/drd.h b/include/linux/usb/drd.h
new file mode 100644
index 0000000..71c64dc
--- /dev/null
+++ b/include/linux/usb/drd.h
@@ -0,0 +1,77 @@
+#include <linux/usb/gadget.h>
+#include <linux/usb/otg.h>
+#include <linux/usb/hcd.h>
+
+struct usb_drd_setup {
+ int (*ll_start)(void *);
+ int (*ll_stop)(void *);
+ void (*ll_release)(struct device *);
+ void *data;
+};
+
+struct usb_drd_host {
+ struct usb_hcd *main_hcd;
+ struct usb_hcd *shared_hcd;
+ int hcd_irq;
+ struct usb_drd_setup *host_setup;
+};
+
+struct usb_drd_gadget {
+ struct usb_gadget_driver *g_driver;
+ struct usb_gadget *gadget;
+ struct usb_drd_setup *gadget_setup;
+};
+
+#define DRD_UNREGISTERED 0x0
+#define DRD_DEVICE_REGISTERED 0x1
+#define DRD_HOST_REGISTERED 0x2
+#define DRD_HOST_ACTIVE 0x4
+#define DRD_DEVICE_ACTIVE 0x8
+
+#if IS_ENABLED(CONFIG_DRD_LIB)
+int usb_drd_release(struct device *parent);
+int usb_drd_add(struct device *parent);
+int usb_drd_register_udc(struct device *parent,
+ struct usb_drd_gadget *gadget);
+int usb_drd_register_udc_driver(struct device *parent,
+ struct usb_gadget_driver *driver);
+int usb_drd_unregister_udc(struct device *parent);
+int usb_drd_unregister_udc_driver(struct device *parent);
+int usb_drd_register_hcd(struct device *parent,
+ struct usb_drd_host *host);
+int usb_drd_unregister_hcd(struct device *parent);
+int usb_drd_start_hcd(struct device *parent);
+int usb_drd_stop_hcd(struct device *parent);
+int usb_drd_start_udc(struct device *parent);
+int usb_drd_stop_udc(struct device *parent);
+int usb_drd_get_state(struct device *parent);
+#else
+static inline int usb_drd_release(struct device *parent)
+{ return 0; }
+static inline int usb_drd_add(struct device *parent)
+{ return 0; }
+static inline int usb_drd_register_udc(struct device *parent,
+ struct usb_drd_gadget *gadget)
+{ return 0; }
+static inline int usb_drd_register_udc_driver(struct device *parent,
+ struct usb_gadget_driver *driver)
+{ return 0; }
+static inline int usb_drd_unregister_udc(struct device *parent,
+ struct usb_drd_gadget *gadget)
+{ return 0; }
+static inline int usb_drd_unregister_udc_driver(struct device *parent)
+{ return 0; }
+static inline int usb_drd_register_hcd(struct device *parent,
+ struct usb_drd_host *host)
+{ return 0; }
+static inline int usb_drd_unregister_hcd(struct device *parent)
+{ return 0; }
+static inline int usb_drd_stop_hcd(struct device *parent)
+{ return 0; }
+static inline int usb_drd_start_udc(struct device *parent)
+{ return 0; }
+static inline int usb_drd_stop_udc(struct device *parent)
+{ return 0; }
+static inline int usb_drd_get_state(struct device *parent)
+{ return 0; }
+#endif
--
1.8.3.1

2014-11-25 13:15:49

by George Cherian

[permalink] [raw]
Subject: [PATCH 02/19] usb: host xhci: fix up deallocation code

This fixes up the deallocation code in the xhci driver, so that
usb_add_hcd()/usb_remove_hcd() can be called repeatedly without
crashing.

In case of DRD mode, the DRD library calls /usb_remove_hcd() while
switching from HOST mode to Device mode, but it doesnot call usb_put_hcd().
We need to preserve the already allocated xhci struct for the subsequent
call of usb_add_hcd() from the DRD library.

A new quirk flag XHCI_DRD_SUPPORT is added to differentiate between
normal usb_remove_hcd and drd specific call.

Signed-off-by: George Cherian <[email protected]>
---
drivers/usb/host/xhci.c | 22 ++++++++++++++++------
drivers/usb/host/xhci.h | 1 +
2 files changed, 17 insertions(+), 6 deletions(-)

diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 2a5d45b..d4196f8 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -666,7 +666,8 @@ static void xhci_only_stop_hcd(struct usb_hcd *hcd)
* calls this function when allocation fails in usb_add_hcd(), or
* usb_remove_hcd() is called). So we need to unset xHCI's pointer.
*/
- xhci->shared_hcd = NULL;
+ if (!(xhci->quirks & XHCI_DRD_SUPPORT))
+ xhci->shared_hcd = NULL;
spin_unlock_irq(&xhci->lock);
}

@@ -4815,6 +4816,7 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks)
struct xhci_hcd *xhci;
struct device *dev = hcd->self.controller;
int retval;
+ bool allocated = false;

/* Accept arbitrarily long scatter-gather lists */
hcd->self.sg_tablesize = ~0;
@@ -4826,10 +4828,15 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks)
hcd->self.no_stop_on_short = 1;

if (usb_hcd_is_primary_hcd(hcd)) {
- xhci = kzalloc(sizeof(struct xhci_hcd), GFP_KERNEL);
- if (!xhci)
- return -ENOMEM;
- *((struct xhci_hcd **) hcd->hcd_priv) = xhci;
+ if (*((struct xhci_hcd **)hcd->hcd_priv) == NULL) {
+ xhci = kzalloc(sizeof(struct xhci_hcd), GFP_KERNEL);
+ if (!xhci)
+ return -ENOMEM;
+ *((struct xhci_hcd **)hcd->hcd_priv) = xhci;
+ allocated = true;
+ } else {
+ xhci = *((struct xhci_hcd **)hcd->hcd_priv);
+ }
xhci->main_hcd = hcd;
/* Mark the first roothub as being USB 2.0.
* The xHCI driver will register the USB 3.0 roothub.
@@ -4902,7 +4909,10 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks)
xhci_dbg(xhci, "Called HCD init\n");
return 0;
error:
- kfree(xhci);
+ if (allocated) {
+ *((struct xhci_hcd **)hcd->hcd_priv) = NULL;
+ kfree(xhci);
+ }
return retval;
}
EXPORT_SYMBOL_GPL(xhci_gen_setup);
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index df76d64..2248058 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -1560,6 +1560,7 @@ struct xhci_hcd {
#define XHCI_SPURIOUS_WAKEUP (1 << 18)
/* For controllers with a broken beyond repair streams implementation */
#define XHCI_BROKEN_STREAMS (1 << 19)
+#define XHCI_DRD_SUPPORT (1 << 20)
unsigned int num_active_eps;
unsigned int limit_active_eps;
/* There are two roothubs to keep track of bus suspend info for */
--
1.8.3.1

2014-11-26 06:22:43

by Peter Chen

[permalink] [raw]
Subject: Re: [PATCH 01/19] usb: common: drd-lib: Add DRD lib for USB.

On Tue, Nov 25, 2014 at 06:41:37PM +0530, George Cherian wrote:
> Add USB DRD library. This Library facilitates to
> switch roles between HOST and Device modes.
>
> A DRD should be added to the library using usb_drd_add().
> Register the HOST and UDC using usb_drd_register_hcd/udc().
> Un-Register the HOST and UDC using usb_drd_unregister_hcd/udc().
>
> Depending on the state of IP -
> Call the following to start/stop HOST controller
> usb_drd_start/stop_hcd().
> This internally calls usb_add/remove_hcd() or IP specific low level start/stop
> defined in ll_start/stop
>
> Call the following to start/stop UDC
> usb_drd_start/stop_udc().
> This internally calls udc_start/udc_stop() or IP specific low level start/stop
> defined in ll_start/stop
>
> Signed-off-by: George Cherian <[email protected]>
> ---
> drivers/usb/Kconfig | 15 ++
> drivers/usb/common/Makefile | 1 +
> drivers/usb/common/drd-lib.c | 346 +++++++++++++++++++++++++++++++++++++++++++
> include/linux/usb/drd.h | 77 ++++++++++
> 4 files changed, 439 insertions(+)
> create mode 100644 drivers/usb/common/drd-lib.c
> create mode 100644 include/linux/usb/drd.h
>
> diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
> index ae481c3..ea0d944 100644
> --- a/drivers/usb/Kconfig
> +++ b/drivers/usb/Kconfig
> @@ -34,6 +34,21 @@ config USB_COMMON
> default y
> depends on USB || USB_GADGET
>
> +config DRD_LIB
> + tristate "DRD Library support"
> + default y
> + depends on USB && USB_GADGET
> + ---help---
> + This option adds DRD Library support for Universal Serial Bus (USB).
> + DRD Library faciliatets the Role switching by HOST and DEVICE roles,
> + If your hardware has a Dual Role Device.
> +
> + The DRD Library uses USB core API's to start/stop HOST controllers,
> + UDC API's to start/stop DEVICE controllers, ther by enabling to
> + switch roles between HOST and Device modes.

%s/ther by/thereby

> +
> + Say N if unsure.
> +
> config USB_ARCH_HAS_HCD
> def_bool y
>
> diff --git a/drivers/usb/common/Makefile b/drivers/usb/common/Makefile
> index ca2f8bd..e2c1593 100644
> --- a/drivers/usb/common/Makefile
> +++ b/drivers/usb/common/Makefile
> @@ -7,3 +7,4 @@ usb-common-y += common.o
> usb-common-$(CONFIG_USB_LED_TRIG) += led.o
>
> obj-$(CONFIG_USB_OTG_FSM) += usb-otg-fsm.o
> +obj-$(CONFIG_DRD_LIB) += drd-lib.o
> diff --git a/drivers/usb/common/drd-lib.c b/drivers/usb/common/drd-lib.c
> new file mode 100644
> index 0000000..6159436
> --- /dev/null
> +++ b/drivers/usb/common/drd-lib.c
> @@ -0,0 +1,346 @@
> +/**
> + * drd-lib.c - USB DRD library functions
> + *
> + * Copyright (C) 2014 Texas Instruments
> + * Author: George Cherian <[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 of
> + * the License 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.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program. If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <linux/device.h>
> +#include <linux/err.h>
> +#include <linux/kernel.h>
> +#include <linux/list.h>
> +#include <linux/module.h>
> +#include <linux/usb.h>
> +
> +#include <linux/usb/hcd.h>
> +#include <linux/usb/gadget.h>
> +#include <linux/usb/drd.h>
> +
> +/**
> + * struct usb_drd - describes one dual role device
> + * @host - the HOST controller device of this drd
> + * @gadget - the gadget of drd
> + * @parent - the device to the actual controller
> + * @list - for use by the drd lib
> + * @state - specifies the current state
> + *
> + * This represents the internal data structure which is used by the UDC-class
> + * to hold information about udc driver and gadget together.
> + */

It is dual role struct, why you only talk about device?

> +struct usb_drd {
> + struct usb_drd_host *host;
> + struct usb_drd_gadget *gadget;
> + struct device *parent;
> + struct list_head list;
> + unsigned int state;
> +};
> +
> +static LIST_HEAD(drd_list);
> +static DEFINE_SPINLOCK(drd_lock);
> +
> +static struct usb_drd *usb_drd_get_dev(struct device *parent)
> +{
> + struct usb_drd *drd;
> +
> + spin_lock(&drd_lock);
> + list_for_each_entry(drd, &drd_list, list)
> + if (drd->parent == parent)
> + goto out;
> + drd = NULL;
> +out:
> + spin_unlock(&drd_lock);
> +
> + return drd;
> +}
> +
> +int usb_drd_get_state(struct device *parent)
> +{
> + struct usb_drd *drd;
> +
> + drd = usb_drd_get_dev(parent);
> + if (!drd)
> + return -ENODEV;
> +
> + return drd->state;
> +}
> +EXPORT_SYMBOL_GPL(usb_drd_get_state);
> +
> +int usb_drd_release(struct device *parent)
> +{
> + struct usb_drd *drd;
> + int ret;
> +
> + spin_lock(&drd_lock);
> + list_for_each_entry(drd, &drd_list, list) {
> + if (drd->parent == parent) {
> + kfree(drd);
> + ret = 0;
> + goto out;
> + }
> + }
> + ret = -ENODEV;
> +out:
> + spin_unlock(&drd_lock);
> +
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(usb_drd_release);
> +
> +int usb_drd_add(struct device *parent)
> +{
> + struct usb_drd *drd;
> +
> + drd = kzalloc(sizeof(*drd), GFP_KERNEL);
> + if (!drd)
> + return -ENOMEM;
> +
> + spin_lock(&drd_lock);
> + drd->parent = parent;
> + list_add_tail(&drd->list, &drd_list);
> + drd->state = DRD_UNREGISTERED;
> +
> + spin_unlock(&drd_lock);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(usb_drd_add);
> +
> +int usb_drd_register_hcd(struct device *parent, struct usb_drd_host *host)
> +{
> + struct usb_drd *drd;
> +
> + drd = usb_drd_get_dev(parent);
> + if (!drd)
> + return -ENODEV;
> +
> + spin_lock(&drd_lock);
> + drd->host = host;
> + drd->state |= DRD_HOST_REGISTERED | DRD_HOST_ACTIVE;
> + spin_unlock(&drd_lock);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(usb_drd_register_hcd);
> +
> +int usb_drd_unregister_hcd(struct device *parent)
> +{
> + struct usb_drd *drd;
> +
> + drd = usb_drd_get_dev(parent);
> + if (!drd)
> + return -ENODEV;
> +
> + spin_lock(&drd_lock);
> + drd->state &= ~(DRD_HOST_REGISTERED | DRD_HOST_ACTIVE);
> + spin_unlock(&drd_lock);
> + kfree(drd->host);

It is better to allocate and free memory at the same file, either
both at DRD LIB APIs, or at host controller and udc driver.

> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(usb_drd_unregister_hcd);
> +
> +int usb_drd_start_hcd(struct device *parent)
> +{
> + struct usb_drd *drd;
> + struct usb_drd_setup *setup;
> +
> + drd = usb_drd_get_dev(parent);
> + if (!drd)
> + return -ENODEV;
> +
> + if (WARN_ON(!(drd->state & DRD_HOST_REGISTERED)))
> + return -EINVAL;
> +
> + setup = drd->host->host_setup;
> + if (setup && setup->ll_start)
> + setup->ll_start(setup->data);
> +
> + usb_add_hcd(drd->host->main_hcd,
> + drd->host->hcd_irq, IRQF_SHARED);
> + if (drd->host->shared_hcd)
> + usb_add_hcd(drd->host->shared_hcd,
> + drd->host->hcd_irq, IRQF_SHARED);

Check return value please.

Peter
> +
> + spin_lock(&drd_lock);
> + drd->state |= DRD_HOST_ACTIVE;
> + spin_unlock(&drd_lock);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(usb_drd_start_hcd);
> +
> +int usb_drd_stop_hcd(struct device *parent)
> +{
> + struct usb_drd *drd;
> + struct usb_drd_setup *setup;
> +
> + drd = usb_drd_get_dev(parent);
> + if (!drd)
> + return -ENODEV;
> +
> + if (WARN_ON(!(drd->state & DRD_HOST_ACTIVE)))
> + return -EINVAL;
> +
> + setup = drd->host->host_setup;
> + if (setup && setup->ll_stop)
> + setup->ll_stop(setup->data);
> + if (drd->host->shared_hcd)
> + usb_remove_hcd(drd->host->shared_hcd);
> +
> + usb_remove_hcd(drd->host->main_hcd);
> +
> + spin_lock(&drd_lock);
> + drd->state = drd->state & ~DRD_HOST_ACTIVE;
> + spin_unlock(&drd_lock);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(usb_drd_stop_hcd);
> +
> +int usb_drd_register_udc(struct device *parent, struct usb_drd_gadget *gadget)
> +{
> + struct usb_drd *drd;
> +
> + drd = usb_drd_get_dev(parent);
> + if (!drd)
> + return -ENODEV;
> +
> + spin_lock(&drd_lock);
> + drd->gadget = gadget;
> + drd->state |= DRD_DEVICE_REGISTERED;
> + if (drd->gadget->g_driver)
> + drd->state |= DRD_DEVICE_ACTIVE;
> +
> + spin_unlock(&drd_lock);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(usb_drd_register_udc);
> +
> +int usb_drd_register_udc_driver(struct device *parent,
> + struct usb_gadget_driver *driver)
> +{
> + struct usb_drd *drd;
> +
> + drd = usb_drd_get_dev(parent);
> + if (!drd)
> + return -ENODEV;
> +
> + spin_lock(&drd_lock);
> + drd->gadget->g_driver = driver;
> + drd->state |= DRD_DEVICE_ACTIVE;
> + spin_unlock(&drd_lock);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(usb_drd_register_udc_driver);
> +
> +int usb_drd_unregister_udc(struct device *parent)
> +{
> + struct usb_drd *drd;
> +
> + drd = usb_drd_get_dev(parent);
> + if (!drd)
> + return -ENODEV;
> +
> + spin_lock(&drd_lock);
> + drd->state &= ~(DRD_DEVICE_REGISTERED | DRD_DEVICE_ACTIVE);
> + spin_unlock(&drd_lock);
> + kfree(drd->gadget->gadget_setup);
> + kfree(drd->gadget);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(usb_drd_unregister_udc);
> +
> +int usb_drd_unregister_udc_driver(struct device *parent)
> +{
> + struct usb_drd *drd;
> + struct usb_drd_gadget *drd_gadget;
> +
> + drd = usb_drd_get_dev(parent);
> + if (!drd)
> + return -ENODEV;
> + drd_gadget = drd->gadget;
> +
> + spin_lock(&drd_lock);
> + drd->state &= ~DRD_DEVICE_ACTIVE;
> + drd_gadget->g_driver = NULL;
> + spin_unlock(&drd_lock);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(usb_drd_unregister_udc_driver);
> +
> +int usb_drd_start_udc(struct device *parent)
> +{
> + struct usb_drd *drd;
> + struct usb_drd_gadget *drd_gadget;
> + struct usb_drd_setup *setup;
> +
> + drd = usb_drd_get_dev(parent);
> + if (!drd)
> + return -ENODEV;
> +
> + if (WARN_ON(!(drd->state & DRD_DEVICE_REGISTERED)))
> + return -EINVAL;
> +
> + drd_gadget = drd->gadget;
> + setup = drd_gadget->gadget_setup;
> +
> + if (setup && setup->ll_start)
> + setup->ll_start(setup->data);
> +
> + usb_add_gadget_udc_release(parent, drd_gadget->gadget,
> + setup->ll_release);
> + spin_lock(&drd_lock);
> + drd->state |= DRD_DEVICE_ACTIVE;
> + spin_unlock(&drd_lock);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(usb_drd_start_udc);
> +
> +int usb_drd_stop_udc(struct device *parent)
> +{
> + struct usb_drd *drd;
> + struct usb_drd_gadget *drd_gadget;
> + struct usb_drd_setup *setup;
> +
> + drd = usb_drd_get_dev(parent);
> + if (!drd)
> + return -ENODEV;
> +
> + if (WARN_ON(!(drd->state & DRD_DEVICE_REGISTERED)))
> + return -EINVAL;
> +
> + drd_gadget = drd->gadget;
> + setup = drd_gadget->gadget_setup;
> + if (setup && setup->ll_stop)
> + setup->ll_stop(setup->data);
> +
> + usb_del_gadget_udc(drd_gadget->gadget);
> +
> + spin_lock(&drd_lock);
> + drd->state = drd->state & ~DRD_DEVICE_ACTIVE;
> + spin_unlock(&drd_lock);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(usb_drd_stop_udc);
> +
> +MODULE_DESCRIPTION("USB-DRD Library");
> +MODULE_AUTHOR("George Cherian <[email protected]>");
> +MODULE_LICENSE("GPL v2");
> diff --git a/include/linux/usb/drd.h b/include/linux/usb/drd.h
> new file mode 100644
> index 0000000..71c64dc
> --- /dev/null
> +++ b/include/linux/usb/drd.h
> @@ -0,0 +1,77 @@
> +#include <linux/usb/gadget.h>
> +#include <linux/usb/otg.h>
> +#include <linux/usb/hcd.h>
> +
> +struct usb_drd_setup {
> + int (*ll_start)(void *);
> + int (*ll_stop)(void *);
> + void (*ll_release)(struct device *);
> + void *data;
> +};
> +
> +struct usb_drd_host {
> + struct usb_hcd *main_hcd;
> + struct usb_hcd *shared_hcd;
> + int hcd_irq;
> + struct usb_drd_setup *host_setup;
> +};
> +
> +struct usb_drd_gadget {
> + struct usb_gadget_driver *g_driver;
> + struct usb_gadget *gadget;
> + struct usb_drd_setup *gadget_setup;
> +};
> +
> +#define DRD_UNREGISTERED 0x0
> +#define DRD_DEVICE_REGISTERED 0x1
> +#define DRD_HOST_REGISTERED 0x2
> +#define DRD_HOST_ACTIVE 0x4
> +#define DRD_DEVICE_ACTIVE 0x8
> +
> +#if IS_ENABLED(CONFIG_DRD_LIB)
> +int usb_drd_release(struct device *parent);
> +int usb_drd_add(struct device *parent);
> +int usb_drd_register_udc(struct device *parent,
> + struct usb_drd_gadget *gadget);
> +int usb_drd_register_udc_driver(struct device *parent,
> + struct usb_gadget_driver *driver);
> +int usb_drd_unregister_udc(struct device *parent);
> +int usb_drd_unregister_udc_driver(struct device *parent);
> +int usb_drd_register_hcd(struct device *parent,
> + struct usb_drd_host *host);
> +int usb_drd_unregister_hcd(struct device *parent);
> +int usb_drd_start_hcd(struct device *parent);
> +int usb_drd_stop_hcd(struct device *parent);
> +int usb_drd_start_udc(struct device *parent);
> +int usb_drd_stop_udc(struct device *parent);
> +int usb_drd_get_state(struct device *parent);
> +#else
> +static inline int usb_drd_release(struct device *parent)
> +{ return 0; }
> +static inline int usb_drd_add(struct device *parent)
> +{ return 0; }
> +static inline int usb_drd_register_udc(struct device *parent,
> + struct usb_drd_gadget *gadget)
> +{ return 0; }
> +static inline int usb_drd_register_udc_driver(struct device *parent,
> + struct usb_gadget_driver *driver)
> +{ return 0; }
> +static inline int usb_drd_unregister_udc(struct device *parent,
> + struct usb_drd_gadget *gadget)
> +{ return 0; }
> +static inline int usb_drd_unregister_udc_driver(struct device *parent)
> +{ return 0; }
> +static inline int usb_drd_register_hcd(struct device *parent,
> + struct usb_drd_host *host)
> +{ return 0; }
> +static inline int usb_drd_unregister_hcd(struct device *parent)
> +{ return 0; }
> +static inline int usb_drd_stop_hcd(struct device *parent)
> +{ return 0; }
> +static inline int usb_drd_start_udc(struct device *parent)
> +{ return 0; }
> +static inline int usb_drd_stop_udc(struct device *parent)
> +{ return 0; }
> +static inline int usb_drd_get_state(struct device *parent)
> +{ return 0; }
> +#endif
> --
> 1.8.3.1
>

--

Best Regards,
Peter Chen

2014-11-27 02:00:39

by Baolu Lu

[permalink] [raw]
Subject: Re: [PATCH 06/19] usb: dwc3: host: Pass the XHCI_DRD_SUPPORT and XHCI_NEEDS_LHC_RESET quirk


On 2014年11月25日 21:11, George Cherian wrote:
> Pass the quir flag XHCI_DRD_SUPPORT from DWC3 host to xhci platform driver.
"quir" to "quirk"

Regards,
Baolu

> This enables xhci driver to handle deallocation's differently while in DRD mode.
> Pass the quirk flag XHCI_NEEDS_LHC_RESET from DWC3 host to xhci platform
> driver. This enables to do LHRESET during xhci_reset().
>
> Signed-off-by: George Cherian <[email protected]>
> ---
> drivers/usb/dwc3/host.c | 2 ++
> 1 file changed, 2 insertions(+)
>
> diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c
> index dcb8ca0..257b5b5 100644
> --- a/drivers/usb/dwc3/host.c
> +++ b/drivers/usb/dwc3/host.c
> @@ -53,6 +53,8 @@ int dwc3_host_init(struct dwc3 *dwc)
> #ifdef CONFIG_DWC3_HOST_USB3_LPM_ENABLE
> pdata.usb3_lpm_capable = 1;
> #endif
> + pdata.usb_drd_support = 1;
> + pdata.usb_needs_lhc_reset = 1;
>
> ret = platform_device_add_data(xhci, &pdata, sizeof(pdata));
> if (ret) {