aboutsummaryrefslogtreecommitdiff
path: root/drivers/usb/dwc3
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/dwc3')
-rw-r--r--drivers/usb/dwc3/Kconfig1
-rw-r--r--drivers/usb/dwc3/core.c201
-rw-r--r--drivers/usb/dwc3/core.h72
-rw-r--r--drivers/usb/dwc3/debugfs.c52
-rw-r--r--drivers/usb/dwc3/dwc3-exynos.c98
-rw-r--r--drivers/usb/dwc3/dwc3-omap.c245
-rw-r--r--drivers/usb/dwc3/dwc3-pci.c38
-rw-r--r--drivers/usb/dwc3/ep0.c33
-rw-r--r--drivers/usb/dwc3/gadget.c321
9 files changed, 746 insertions, 315 deletions
diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig
index 68e9a2c5a01..ea5ee9c21c3 100644
--- a/drivers/usb/dwc3/Kconfig
+++ b/drivers/usb/dwc3/Kconfig
@@ -1,7 +1,6 @@
config USB_DWC3
tristate "DesignWare USB3 DRD Core Support"
depends on (USB || USB_GADGET) && GENERIC_HARDIRQS
- select USB_OTG_UTILS
select USB_XHCI_PLATFORM if USB_SUPPORT && USB_XHCI_HCD
help
Say Y or M here if your system has a Dual Role SuperSpeed
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index ffa6b004a84..c35d49d39b7 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -140,7 +140,8 @@ static void dwc3_free_one_event_buffer(struct dwc3 *dwc,
* Returns a pointer to the allocated event buffer structure on success
* otherwise ERR_PTR(errno).
*/
-static struct dwc3_event_buffer *dwc3_alloc_one_event_buffer(struct dwc3 *dwc, unsigned length)
+static struct dwc3_event_buffer *dwc3_alloc_one_event_buffer(struct dwc3 *dwc,
+ unsigned length)
{
struct dwc3_event_buffer *evt;
@@ -259,6 +260,17 @@ static void dwc3_event_buffers_cleanup(struct dwc3 *dwc)
}
}
+static void dwc3_core_num_eps(struct dwc3 *dwc)
+{
+ struct dwc3_hwparams *parms = &dwc->hwparams;
+
+ dwc->num_in_eps = DWC3_NUM_IN_EPS(parms);
+ dwc->num_out_eps = DWC3_NUM_EPS(parms) - dwc->num_in_eps;
+
+ dev_vdbg(dwc->dev, "found %d IN and %d OUT endpoints\n",
+ dwc->num_in_eps, dwc->num_out_eps);
+}
+
static void dwc3_cache_hwparams(struct dwc3 *dwc)
{
struct dwc3_hwparams *parms = &dwc->hwparams;
@@ -335,13 +347,9 @@ static int dwc3_core_init(struct dwc3 *dwc)
if (dwc->revision < DWC3_REVISION_190A)
reg |= DWC3_GCTL_U2RSTECN;
- dwc3_writel(dwc->regs, DWC3_GCTL, reg);
+ dwc3_core_num_eps(dwc);
- ret = dwc3_event_buffers_setup(dwc);
- if (ret) {
- dev_err(dwc->dev, "failed to setup event buffers\n");
- goto err0;
- }
+ dwc3_writel(dwc->regs, DWC3_GCTL, reg);
return 0;
@@ -351,8 +359,6 @@ err0:
static void dwc3_core_exit(struct dwc3 *dwc)
{
- dwc3_event_buffers_cleanup(dwc);
-
usb_phy_shutdown(dwc->usb2_phy);
usb_phy_shutdown(dwc->usb3_phy);
}
@@ -428,12 +434,32 @@ static int dwc3_probe(struct platform_device *pdev)
dwc->usb3_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB3);
}
- if (IS_ERR_OR_NULL(dwc->usb2_phy)) {
+ if (IS_ERR(dwc->usb2_phy)) {
+ ret = PTR_ERR(dwc->usb2_phy);
+
+ /*
+ * if -ENXIO is returned, it means PHY layer wasn't
+ * enabled, so it makes no sense to return -EPROBE_DEFER
+ * in that case, since no PHY driver will ever probe.
+ */
+ if (ret == -ENXIO)
+ return ret;
+
dev_err(dev, "no usb2 phy configured\n");
return -EPROBE_DEFER;
}
- if (IS_ERR_OR_NULL(dwc->usb3_phy)) {
+ if (IS_ERR(dwc->usb3_phy)) {
+ ret = PTR_ERR(dwc->usb2_phy);
+
+ /*
+ * if -ENXIO is returned, it means PHY layer wasn't
+ * enabled, so it makes no sense to return -EPROBE_DEFER
+ * in that case, since no PHY driver will ever probe.
+ */
+ if (ret == -ENXIO)
+ return ret;
+
dev_err(dev, "no usb3 phy configured\n");
return -EPROBE_DEFER;
}
@@ -448,6 +474,10 @@ static int dwc3_probe(struct platform_device *pdev)
dwc->regs_size = resource_size(res);
dwc->dev = dev;
+ dev->dma_mask = dev->parent->dma_mask;
+ dev->dma_parms = dev->parent->dma_parms;
+ dma_set_coherent_mask(dev, dev->parent->coherent_dma_mask);
+
if (!strncmp("super", maximum_speed, 5))
dwc->maximum_speed = DWC3_DCFG_SUPERSPEED;
else if (!strncmp("high", maximum_speed, 4))
@@ -480,7 +510,18 @@ static int dwc3_probe(struct platform_device *pdev)
goto err0;
}
- mode = DWC3_MODE(dwc->hwparams.hwparams0);
+ ret = dwc3_event_buffers_setup(dwc);
+ if (ret) {
+ dev_err(dwc->dev, "failed to setup event buffers\n");
+ goto err1;
+ }
+
+ if (IS_ENABLED(CONFIG_USB_DWC3_HOST))
+ mode = DWC3_MODE_HOST;
+ else if (IS_ENABLED(CONFIG_USB_DWC3_GADGET))
+ mode = DWC3_MODE_DEVICE;
+ else
+ mode = DWC3_MODE_DRD;
switch (mode) {
case DWC3_MODE_DEVICE:
@@ -488,7 +529,7 @@ static int dwc3_probe(struct platform_device *pdev)
ret = dwc3_gadget_init(dwc);
if (ret) {
dev_err(dev, "failed to initialize gadget\n");
- goto err1;
+ goto err2;
}
break;
case DWC3_MODE_HOST:
@@ -496,7 +537,7 @@ static int dwc3_probe(struct platform_device *pdev)
ret = dwc3_host_init(dwc);
if (ret) {
dev_err(dev, "failed to initialize host\n");
- goto err1;
+ goto err2;
}
break;
case DWC3_MODE_DRD:
@@ -504,32 +545,32 @@ static int dwc3_probe(struct platform_device *pdev)
ret = dwc3_host_init(dwc);
if (ret) {
dev_err(dev, "failed to initialize host\n");
- goto err1;
+ goto err2;
}
ret = dwc3_gadget_init(dwc);
if (ret) {
dev_err(dev, "failed to initialize gadget\n");
- goto err1;
+ goto err2;
}
break;
default:
dev_err(dev, "Unsupported mode of operation %d\n", mode);
- goto err1;
+ goto err2;
}
dwc->mode = mode;
ret = dwc3_debugfs_init(dwc);
if (ret) {
dev_err(dev, "failed to initialize debugfs\n");
- goto err2;
+ goto err3;
}
pm_runtime_allow(dev);
return 0;
-err2:
+err3:
switch (mode) {
case DWC3_MODE_DEVICE:
dwc3_gadget_exit(dwc);
@@ -546,6 +587,9 @@ err2:
break;
}
+err2:
+ dwc3_event_buffers_cleanup(dwc);
+
err1:
dwc3_core_exit(dwc);
@@ -583,12 +627,130 @@ static int dwc3_remove(struct platform_device *pdev)
break;
}
+ dwc3_event_buffers_cleanup(dwc);
dwc3_free_event_buffers(dwc);
dwc3_core_exit(dwc);
return 0;
}
+#ifdef CONFIG_PM_SLEEP
+static int dwc3_prepare(struct device *dev)
+{
+ struct dwc3 *dwc = dev_get_drvdata(dev);
+ unsigned long flags;
+
+ spin_lock_irqsave(&dwc->lock, flags);
+
+ switch (dwc->mode) {
+ case DWC3_MODE_DEVICE:
+ case DWC3_MODE_DRD:
+ dwc3_gadget_prepare(dwc);
+ /* FALLTHROUGH */
+ case DWC3_MODE_HOST:
+ default:
+ dwc3_event_buffers_cleanup(dwc);
+ break;
+ }
+
+ spin_unlock_irqrestore(&dwc->lock, flags);
+
+ return 0;
+}
+
+static void dwc3_complete(struct device *dev)
+{
+ struct dwc3 *dwc = dev_get_drvdata(dev);
+ unsigned long flags;
+
+ spin_lock_irqsave(&dwc->lock, flags);
+
+ switch (dwc->mode) {
+ case DWC3_MODE_DEVICE:
+ case DWC3_MODE_DRD:
+ dwc3_gadget_complete(dwc);
+ /* FALLTHROUGH */
+ case DWC3_MODE_HOST:
+ default:
+ dwc3_event_buffers_setup(dwc);
+ break;
+ }
+
+ spin_unlock_irqrestore(&dwc->lock, flags);
+}
+
+static int dwc3_suspend(struct device *dev)
+{
+ struct dwc3 *dwc = dev_get_drvdata(dev);
+ unsigned long flags;
+
+ spin_lock_irqsave(&dwc->lock, flags);
+
+ switch (dwc->mode) {
+ case DWC3_MODE_DEVICE:
+ case DWC3_MODE_DRD:
+ dwc3_gadget_suspend(dwc);
+ /* FALLTHROUGH */
+ case DWC3_MODE_HOST:
+ default:
+ /* do nothing */
+ break;
+ }
+
+ dwc->gctl = dwc3_readl(dwc->regs, DWC3_GCTL);
+ spin_unlock_irqrestore(&dwc->lock, flags);
+
+ usb_phy_shutdown(dwc->usb3_phy);
+ usb_phy_shutdown(dwc->usb2_phy);
+
+ return 0;
+}
+
+static int dwc3_resume(struct device *dev)
+{
+ struct dwc3 *dwc = dev_get_drvdata(dev);
+ unsigned long flags;
+
+ usb_phy_init(dwc->usb3_phy);
+ usb_phy_init(dwc->usb2_phy);
+ msleep(100);
+
+ spin_lock_irqsave(&dwc->lock, flags);
+
+ dwc3_writel(dwc->regs, DWC3_GCTL, dwc->gctl);
+
+ switch (dwc->mode) {
+ case DWC3_MODE_DEVICE:
+ case DWC3_MODE_DRD:
+ dwc3_gadget_resume(dwc);
+ /* FALLTHROUGH */
+ case DWC3_MODE_HOST:
+ default:
+ /* do nothing */
+ break;
+ }
+
+ spin_unlock_irqrestore(&dwc->lock, flags);
+
+ pm_runtime_disable(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+
+ return 0;
+}
+
+static const struct dev_pm_ops dwc3_dev_pm_ops = {
+ .prepare = dwc3_prepare,
+ .complete = dwc3_complete,
+
+ SET_SYSTEM_SLEEP_PM_OPS(dwc3_suspend, dwc3_resume)
+};
+
+#define DWC3_PM_OPS &(dwc3_dev_pm_ops)
+#else
+#define DWC3_PM_OPS NULL
+#endif
+
#ifdef CONFIG_OF
static const struct of_device_id of_dwc3_match[] = {
{
@@ -605,6 +767,7 @@ static struct platform_driver dwc3_driver = {
.driver = {
.name = "dwc3",
.of_match_table = of_match_ptr(of_dwc3_match),
+ .pm = DWC3_PM_OPS,
},
};
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index b4175066023..b69d322e3ca 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -154,8 +154,9 @@
/* OTG Registers */
#define DWC3_OCFG 0xcc00
#define DWC3_OCTL 0xcc04
-#define DWC3_OEVTEN 0xcc08
-#define DWC3_OSTS 0xcc0C
+#define DWC3_OEVT 0xcc08
+#define DWC3_OEVTEN 0xcc0C
+#define DWC3_OSTS 0xcc10
/* Bit fields */
@@ -369,6 +370,9 @@ struct dwc3_trb;
* @list: a list of event buffers
* @buf: _THE_ buffer
* @length: size of this buffer
+ * @lpos: event offset
+ * @count: cache of last read event count register
+ * @flags: flags related to this event buffer
* @dma: dma_addr_t
* @dwc: pointer to DWC controller
*/
@@ -376,6 +380,10 @@ struct dwc3_event_buffer {
void *buf;
unsigned length;
unsigned int lpos;
+ unsigned int count;
+ unsigned int flags;
+
+#define DWC3_EVENT_PENDING BIT(0)
dma_addr_t dma;
@@ -487,12 +495,6 @@ enum dwc3_link_state {
DWC3_LINK_STATE_MASK = 0x0f,
};
-enum dwc3_device_state {
- DWC3_DEFAULT_STATE,
- DWC3_ADDRESS_STATE,
- DWC3_CONFIGURED_STATE,
-};
-
/* TRB Length, PCM and Status */
#define DWC3_TRB_SIZE_MASK (0x00ffffff)
#define DWC3_TRB_SIZE_LENGTH(n) ((n) & DWC3_TRB_SIZE_MASK)
@@ -574,6 +576,14 @@ struct dwc3_hwparams {
/* HWPARAMS1 */
#define DWC3_NUM_INT(n) (((n) & (0x3f << 15)) >> 15)
+/* HWPARAMS3 */
+#define DWC3_NUM_IN_EPS_MASK (0x1f << 18)
+#define DWC3_NUM_EPS_MASK (0x3f << 12)
+#define DWC3_NUM_EPS(p) (((p)->hwparams3 & \
+ (DWC3_NUM_EPS_MASK)) >> 12)
+#define DWC3_NUM_IN_EPS(p) (((p)->hwparams3 & \
+ (DWC3_NUM_IN_EPS_MASK)) >> 18)
+
/* HWPARAMS7 */
#define DWC3_RAM1_DEPTH(n) ((n) & 0xffff)
@@ -618,7 +628,6 @@ struct dwc3_scratchpad_array {
* @gadget_driver: pointer to the gadget driver
* @regs: base address for our registers
* @regs_size: address space size
- * @irq: IRQ number
* @num_event_buffers: calculated number of event buffers
* @u1u2: only used on revisions <1.83a for workaround
* @maximum_speed: maximum speed requested (mainly for testing purposes)
@@ -626,6 +635,8 @@ struct dwc3_scratchpad_array {
* @mode: mode of operation
* @usb2_phy: pointer to USB2 PHY
* @usb3_phy: pointer to USB3 PHY
+ * @dcfg: saved contents of DCFG register
+ * @gctl: saved contents of GCTL register
* @is_selfpowered: true when we are selfpowered
* @three_stage_setup: set if we perform a three phase setup
* @ep0_bounced: true when we used bounce buffer
@@ -639,6 +650,8 @@ struct dwc3_scratchpad_array {
* @u2pel: parameter from Set SEL request.
* @u1sel: parameter from Set SEL request.
* @u1pel: parameter from Set SEL request.
+ * @num_out_eps: number of out endpoints
+ * @num_in_eps: number of in endpoints
* @ep0_next_event: hold the next expected event
* @ep0state: state of endpoint zero
* @link_state: link state
@@ -656,8 +669,10 @@ struct dwc3 {
dma_addr_t ep0_trb_addr;
dma_addr_t ep0_bounce_addr;
struct dwc3_request ep0_usb_req;
+
/* device lock */
spinlock_t lock;
+
struct device *dev;
struct platform_device *xhci;
@@ -675,6 +690,10 @@ struct dwc3 {
void __iomem *regs;
size_t regs_size;
+ /* used for suspend/resume */
+ u32 dcfg;
+ u32 gctl;
+
u32 num_event_buffers;
u32 u1u2;
u32 maximum_speed;
@@ -694,6 +713,9 @@ struct dwc3 {
#define DWC3_REVISION_202A 0x5533202a
#define DWC3_REVISION_210A 0x5533210a
#define DWC3_REVISION_220A 0x5533220a
+#define DWC3_REVISION_230A 0x5533230a
+#define DWC3_REVISION_240A 0x5533240a
+#define DWC3_REVISION_250A 0x5533250a
unsigned is_selfpowered:1;
unsigned three_stage_setup:1;
@@ -704,11 +726,11 @@ struct dwc3 {
unsigned delayed_status:1;
unsigned needs_fifo_resize:1;
unsigned resize_fifos:1;
+ unsigned pullups_connected:1;
enum dwc3_ep0_next ep0_next_event;
enum dwc3_ep0_state ep0state;
enum dwc3_link_state link_state;
- enum dwc3_device_state dev_state;
u16 isoch_delay;
u16 u2sel;
@@ -718,6 +740,9 @@ struct dwc3 {
u8 speed;
+ u8 num_out_eps;
+ u8 num_in_eps;
+
void *mem;
struct dwc3_hwparams hwparams;
@@ -884,4 +909,31 @@ static inline void dwc3_gadget_exit(struct dwc3 *dwc)
{ }
#endif
+/* power management interface */
+#if !IS_ENABLED(CONFIG_USB_DWC3_HOST)
+int dwc3_gadget_prepare(struct dwc3 *dwc);
+void dwc3_gadget_complete(struct dwc3 *dwc);
+int dwc3_gadget_suspend(struct dwc3 *dwc);
+int dwc3_gadget_resume(struct dwc3 *dwc);
+#else
+static inline int dwc3_gadget_prepare(struct dwc3 *dwc)
+{
+ return 0;
+}
+
+static inline void dwc3_gadget_complete(struct dwc3 *dwc)
+{
+}
+
+static inline int dwc3_gadget_suspend(struct dwc3 *dwc)
+{
+ return 0;
+}
+
+static inline int dwc3_gadget_resume(struct dwc3 *dwc)
+{
+ return 0;
+}
+#endif /* !IS_ENABLED(CONFIG_USB_DWC3_HOST) */
+
#endif /* __DRIVERS_USB_DWC3_CORE_H */
diff --git a/drivers/usb/dwc3/debugfs.c b/drivers/usb/dwc3/debugfs.c
index 4a752e730c5..9e9f122162f 100644
--- a/drivers/usb/dwc3/debugfs.c
+++ b/drivers/usb/dwc3/debugfs.c
@@ -59,7 +59,7 @@
.offset = DWC3_ ##nm - DWC3_GLOBALS_REGS_START, \
}
-static struct debugfs_reg32 dwc3_regs[] = {
+static const struct debugfs_reg32 dwc3_regs[] = {
dump_register(GSBUSCFG0),
dump_register(GSBUSCFG1),
dump_register(GTXTHRCFG),
@@ -372,6 +372,7 @@ static struct debugfs_reg32 dwc3_regs[] = {
dump_register(OCFG),
dump_register(OCTL),
+ dump_register(OEVT),
dump_register(OEVTEN),
dump_register(OSTS),
};
@@ -577,8 +578,14 @@ static int dwc3_link_state_show(struct seq_file *s, void *unused)
case DWC3_LINK_STATE_LPBK:
seq_printf(s, "Loopback\n");
break;
+ case DWC3_LINK_STATE_RESET:
+ seq_printf(s, "Reset\n");
+ break;
+ case DWC3_LINK_STATE_RESUME:
+ seq_printf(s, "Resume\n");
+ break;
default:
- seq_printf(s, "UNKNOWN %d\n", reg);
+ seq_printf(s, "UNKNOWN %d\n", state);
}
return 0;
@@ -661,28 +668,31 @@ int dwc3_debugfs_init(struct dwc3 *dwc)
goto err1;
}
-#if IS_ENABLED(CONFIG_USB_DWC3_GADGET)
- file = debugfs_create_file("mode", S_IRUGO | S_IWUSR, root,
- dwc, &dwc3_mode_fops);
- if (!file) {
- ret = -ENOMEM;
- goto err1;
- }
-
- file = debugfs_create_file("testmode", S_IRUGO | S_IWUSR, root,
- dwc, &dwc3_testmode_fops);
- if (!file) {
- ret = -ENOMEM;
- goto err1;
+ if (IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)) {
+ file = debugfs_create_file("mode", S_IRUGO | S_IWUSR, root,
+ dwc, &dwc3_mode_fops);
+ if (!file) {
+ ret = -ENOMEM;
+ goto err1;
+ }
}
- file = debugfs_create_file("link_state", S_IRUGO | S_IWUSR, root,
- dwc, &dwc3_link_state_fops);
- if (!file) {
- ret = -ENOMEM;
- goto err1;
+ if (IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE) ||
+ IS_ENABLED(CONFIG_USB_DWC3_GADGET)) {
+ file = debugfs_create_file("testmode", S_IRUGO | S_IWUSR, root,
+ dwc, &dwc3_testmode_fops);
+ if (!file) {
+ ret = -ENOMEM;
+ goto err1;
+ }
+
+ file = debugfs_create_file("link_state", S_IRUGO | S_IWUSR, root,
+ dwc, &dwc3_link_state_fops);
+ if (!file) {
+ ret = -ENOMEM;
+ goto err1;
+ }
}
-#endif
return 0;
diff --git a/drivers/usb/dwc3/dwc3-exynos.c b/drivers/usb/dwc3/dwc3-exynos.c
index b082bec7343..a8afe6e2662 100644
--- a/drivers/usb/dwc3/dwc3-exynos.c
+++ b/drivers/usb/dwc3/dwc3-exynos.c
@@ -22,9 +22,9 @@
#include <linux/usb/otg.h>
#include <linux/usb/nop-usb-xceiv.h>
#include <linux/of.h>
+#include <linux/of_platform.h>
struct dwc3_exynos {
- struct platform_device *dwc3;
struct platform_device *usb2_phy;
struct platform_device *usb3_phy;
struct device *dev;
@@ -86,21 +86,30 @@ err1:
return ret;
}
+static int dwc3_exynos_remove_child(struct device *dev, void *unused)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+
+ platform_device_unregister(pdev);
+
+ return 0;
+}
+
static u64 dwc3_exynos_dma_mask = DMA_BIT_MASK(32);
static int dwc3_exynos_probe(struct platform_device *pdev)
{
- struct platform_device *dwc3;
struct dwc3_exynos *exynos;
struct clk *clk;
struct device *dev = &pdev->dev;
+ struct device_node *node = dev->of_node;
int ret = -ENOMEM;
exynos = devm_kzalloc(dev, sizeof(*exynos), GFP_KERNEL);
if (!exynos) {
dev_err(dev, "not enough memory\n");
- return -ENOMEM;
+ goto err1;
}
/*
@@ -108,21 +117,15 @@ static int dwc3_exynos_probe(struct platform_device *pdev)
* Since shared usb code relies on it, set it here for now.
* Once we move to full device tree support this will vanish off.
*/
- if (!pdev->dev.dma_mask)
- pdev->dev.dma_mask = &dwc3_exynos_dma_mask;
+ if (!dev->dma_mask)
+ dev->dma_mask = &dwc3_exynos_dma_mask;
platform_set_drvdata(pdev, exynos);
ret = dwc3_exynos_register_phys(exynos);
if (ret) {
dev_err(dev, "couldn't register PHYs\n");
- return ret;
- }
-
- dwc3 = platform_device_alloc("dwc3", PLATFORM_DEVID_AUTO);
- if (!dwc3) {
- dev_err(dev, "couldn't allocate dwc3 device\n");
- return -ENOMEM;
+ goto err1;
}
clk = devm_clk_get(dev, "usbdrd30");
@@ -132,37 +135,28 @@ static int dwc3_exynos_probe(struct platform_device *pdev)
goto err1;
}
- dma_set_coherent_mask(&dwc3->dev, dev->coherent_dma_mask);
-
- dwc3->dev.parent = dev;
- dwc3->dev.dma_mask = dev->dma_mask;
- dwc3->dev.dma_parms = dev->dma_parms;
- exynos->dwc3 = dwc3;
exynos->dev = dev;
exynos->clk = clk;
- clk_enable(exynos->clk);
-
- ret = platform_device_add_resources(dwc3, pdev->resource,
- pdev->num_resources);
- if (ret) {
- dev_err(dev, "couldn't add resources to dwc3 device\n");
- goto err2;
- }
-
- ret = platform_device_add(dwc3);
- if (ret) {
- dev_err(dev, "failed to register dwc3 device\n");
+ clk_prepare_enable(exynos->clk);
+
+ if (node) {
+ ret = of_platform_populate(node, NULL, NULL, dev);
+ if (ret) {
+ dev_err(dev, "failed to add dwc3 core\n");
+ goto err2;
+ }
+ } else {
+ dev_err(dev, "no device node, failed to add dwc3 core\n");
+ ret = -ENODEV;
goto err2;
}
return 0;
err2:
- clk_disable(clk);
+ clk_disable_unprepare(clk);
err1:
- platform_device_put(dwc3);
-
return ret;
}
@@ -170,11 +164,11 @@ static int dwc3_exynos_remove(struct platform_device *pdev)
{
struct dwc3_exynos *exynos = platform_get_drvdata(pdev);
- platform_device_unregister(exynos->dwc3);
platform_device_unregister(exynos->usb2_phy);
platform_device_unregister(exynos->usb3_phy);
+ device_for_each_child(&pdev->dev, NULL, dwc3_exynos_remove_child);
- clk_disable(exynos->clk);
+ clk_disable_unprepare(exynos->clk);
return 0;
}
@@ -187,12 +181,46 @@ static const struct of_device_id exynos_dwc3_match[] = {
MODULE_DEVICE_TABLE(of, exynos_dwc3_match);
#endif
+#ifdef CONFIG_PM_SLEEP
+static int dwc3_exynos_suspend(struct device *dev)
+{
+ struct dwc3_exynos *exynos = dev_get_drvdata(dev);
+
+ clk_disable(exynos->clk);
+
+ return 0;
+}
+
+static int dwc3_exynos_resume(struct device *dev)
+{
+ struct dwc3_exynos *exynos = dev_get_drvdata(dev);
+
+ clk_enable(exynos->clk);
+
+ /* runtime set active to reflect active state. */
+ pm_runtime_disable(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+
+ return 0;
+}
+
+static const struct dev_pm_ops dwc3_exynos_dev_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(dwc3_exynos_suspend, dwc3_exynos_resume)
+};
+
+#define DEV_PM_OPS (&dwc3_exynos_dev_pm_ops)
+#else
+#define DEV_PM_OPS NULL
+#endif /* CONFIG_PM_SLEEP */
+
static struct platform_driver dwc3_exynos_driver = {
.probe = dwc3_exynos_probe,
.remove = dwc3_exynos_remove,
.driver = {
.name = "exynos-dwc3",
.of_match_table = of_match_ptr(exynos_dwc3_match),
+ .pm = DEV_PM_OPS,
},
};
diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c
index afa05e3c9cf..34638b92500 100644
--- a/drivers/usb/dwc3/dwc3-omap.c
+++ b/drivers/usb/dwc3/dwc3-omap.c
@@ -52,7 +52,6 @@
#include <linux/of_platform.h>
#include <linux/usb/otg.h>
-#include <linux/usb/nop-usb-xceiv.h>
/*
* All these registers belong to OMAP's Wrapper around the
@@ -117,20 +116,17 @@ struct dwc3_omap {
/* device lock */
spinlock_t lock;
- struct platform_device *usb2_phy;
- struct platform_device *usb3_phy;
struct device *dev;
int irq;
void __iomem *base;
- void *context;
- u32 resource_size;
+ u32 utmi_otg_status;
u32 dma_status:1;
};
-struct dwc3_omap *_omap;
+static struct dwc3_omap *_omap;
static inline u32 dwc3_omap_readl(void __iomem *base, u32 offset)
{
@@ -142,11 +138,14 @@ static inline void dwc3_omap_writel(void __iomem *base, u32 offset, u32 value)
writel(value, base + offset);
}
-void dwc3_omap_mailbox(enum omap_dwc3_vbus_id_status status)
+int dwc3_omap_mailbox(enum omap_dwc3_vbus_id_status status)
{
u32 val;
struct dwc3_omap *omap = _omap;
+ if (!omap)
+ return -EPROBE_DEFER;
+
switch (status) {
case OMAP_DWC3_ID_GROUND:
dev_dbg(omap->dev, "ID GND\n");
@@ -189,63 +188,9 @@ void dwc3_omap_mailbox(enum omap_dwc3_vbus_id_status status)
dev_dbg(omap->dev, "ID float\n");
}
- return;
-}
-EXPORT_SYMBOL_GPL(dwc3_omap_mailbox);
-
-static int dwc3_omap_register_phys(struct dwc3_omap *omap)
-{
- struct nop_usb_xceiv_platform_data pdata;
- struct platform_device *pdev;
- int ret;
-
- memset(&pdata, 0x00, sizeof(pdata));
-
- pdev = platform_device_alloc("nop_usb_xceiv", PLATFORM_DEVID_AUTO);
- if (!pdev)
- return -ENOMEM;
-
- omap->usb2_phy = pdev;
- pdata.type = USB_PHY_TYPE_USB2;
-
- ret = platform_device_add_data(omap->usb2_phy, &pdata, sizeof(pdata));
- if (ret)
- goto err1;
-
- pdev = platform_device_alloc("nop_usb_xceiv", PLATFORM_DEVID_AUTO);
- if (!pdev) {
- ret = -ENOMEM;
- goto err1;
- }
-
- omap->usb3_phy = pdev;
- pdata.type = USB_PHY_TYPE_USB3;
-
- ret = platform_device_add_data(omap->usb3_phy, &pdata, sizeof(pdata));
- if (ret)
- goto err2;
-
- ret = platform_device_add(omap->usb2_phy);
- if (ret)
- goto err2;
-
- ret = platform_device_add(omap->usb3_phy);
- if (ret)
- goto err3;
-
return 0;
-
-err3:
- platform_device_del(omap->usb2_phy);
-
-err2:
- platform_device_put(omap->usb3_phy);
-
-err1:
- platform_device_put(omap->usb2_phy);
-
- return ret;
}
+EXPORT_SYMBOL_GPL(dwc3_omap_mailbox);
static irqreturn_t dwc3_omap_interrupt(int irq, void *_omap)
{
@@ -307,24 +252,57 @@ static int dwc3_omap_remove_core(struct device *dev, void *c)
return 0;
}
+static void dwc3_omap_enable_irqs(struct dwc3_omap *omap)
+{
+ u32 reg;
+
+ /* enable all IRQs */
+ reg = USBOTGSS_IRQO_COREIRQ_ST;
+ dwc3_omap_writel(omap->base, USBOTGSS_IRQENABLE_SET_0, reg);
+
+ reg = (USBOTGSS_IRQ1_OEVT |
+ USBOTGSS_IRQ1_DRVVBUS_RISE |
+ USBOTGSS_IRQ1_CHRGVBUS_RISE |
+ USBOTGSS_IRQ1_DISCHRGVBUS_RISE |
+ USBOTGSS_IRQ1_IDPULLUP_RISE |
+ USBOTGSS_IRQ1_DRVVBUS_FALL |
+ USBOTGSS_IRQ1_CHRGVBUS_FALL |
+ USBOTGSS_IRQ1_DISCHRGVBUS_FALL |
+ USBOTGSS_IRQ1_IDPULLUP_FALL);
+
+ dwc3_omap_writel(omap->base, USBOTGSS_IRQENABLE_SET_1, reg);
+}
+
+static void dwc3_omap_disable_irqs(struct dwc3_omap *omap)
+{
+ /* disable all IRQs */
+ dwc3_omap_writel(omap->base, USBOTGSS_IRQENABLE_SET_1, 0x00);
+ dwc3_omap_writel(omap->base, USBOTGSS_IRQENABLE_SET_0, 0x00);
+}
+
+static u64 dwc3_omap_dma_mask = DMA_BIT_MASK(32);
+
static int dwc3_omap_probe(struct platform_device *pdev)
{
- struct dwc3_omap_data *pdata = pdev->dev.platform_data;
struct device_node *node = pdev->dev.of_node;
struct dwc3_omap *omap;
struct resource *res;
struct device *dev = &pdev->dev;
- int size;
int ret = -ENOMEM;
int irq;
- const u32 *utmi_mode;
+ int utmi_mode = 0;
+
u32 reg;
void __iomem *base;
- void *context;
+
+ if (!node) {
+ dev_err(dev, "device node not found\n");
+ return -EINVAL;
+ }
omap = devm_kzalloc(dev, sizeof(*omap), GFP_KERNEL);
if (!omap) {
@@ -334,13 +312,13 @@ static int dwc3_omap_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, omap);
- irq = platform_get_irq(pdev, 1);
+ irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(dev, "missing IRQ resource\n");
return -EINVAL;
}
- res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(dev, "missing memory base resource\n");
return -EINVAL;
@@ -352,25 +330,12 @@ static int dwc3_omap_probe(struct platform_device *pdev)
return -ENOMEM;
}
- ret = dwc3_omap_register_phys(omap);
- if (ret) {
- dev_err(dev, "couldn't register PHYs\n");
- return ret;
- }
-
- context = devm_kzalloc(dev, resource_size(res), GFP_KERNEL);
- if (!context) {
- dev_err(dev, "couldn't allocate dwc3 context memory\n");
- return -ENOMEM;
- }
-
spin_lock_init(&omap->lock);
- omap->resource_size = resource_size(res);
- omap->context = context;
omap->dev = dev;
omap->irq = irq;
omap->base = base;
+ dev->dma_mask = &dwc3_omap_dma_mask;
/*
* REVISIT if we ever have two instances of the wrapper, we will be
@@ -387,25 +352,17 @@ static int dwc3_omap_probe(struct platform_device *pdev)
reg = dwc3_omap_readl(omap->base, USBOTGSS_UTMI_OTG_STATUS);
- utmi_mode = of_get_property(node, "utmi-mode", &size);
- if (utmi_mode && size == sizeof(*utmi_mode)) {
- reg |= *utmi_mode;
- } else {
- if (!pdata) {
- dev_dbg(dev, "missing platform data\n");
- } else {
- switch (pdata->utmi_mode) {
- case DWC3_OMAP_UTMI_MODE_SW:
- reg |= USBOTGSS_UTMI_OTG_STATUS_SW_MODE;
- break;
- case DWC3_OMAP_UTMI_MODE_HW:
- reg &= ~USBOTGSS_UTMI_OTG_STATUS_SW_MODE;
- break;
- default:
- dev_dbg(dev, "UNKNOWN utmi mode %d\n",
- pdata->utmi_mode);
- }
- }
+ of_property_read_u32(node, "utmi-mode", &utmi_mode);
+
+ switch (utmi_mode) {
+ case DWC3_OMAP_UTMI_MODE_SW:
+ reg |= USBOTGSS_UTMI_OTG_STATUS_SW_MODE;
+ break;
+ case DWC3_OMAP_UTMI_MODE_HW:
+ reg &= ~USBOTGSS_UTMI_OTG_STATUS_SW_MODE;
+ break;
+ default:
+ dev_dbg(dev, "UNKNOWN utmi mode %d\n", utmi_mode);
}
dwc3_omap_writel(omap->base, USBOTGSS_UTMI_OTG_STATUS, reg);
@@ -422,29 +379,12 @@ static int dwc3_omap_probe(struct platform_device *pdev)
return ret;
}
- /* enable all IRQs */
- reg = USBOTGSS_IRQO_COREIRQ_ST;
- dwc3_omap_writel(omap->base, USBOTGSS_IRQENABLE_SET_0, reg);
+ dwc3_omap_enable_irqs(omap);
- reg = (USBOTGSS_IRQ1_OEVT |
- USBOTGSS_IRQ1_DRVVBUS_RISE |
- USBOTGSS_IRQ1_CHRGVBUS_RISE |
- USBOTGSS_IRQ1_DISCHRGVBUS_RISE |
- USBOTGSS_IRQ1_IDPULLUP_RISE |
- USBOTGSS_IRQ1_DRVVBUS_FALL |
- USBOTGSS_IRQ1_CHRGVBUS_FALL |
- USBOTGSS_IRQ1_DISCHRGVBUS_FALL |
- USBOTGSS_IRQ1_IDPULLUP_FALL);
-
- dwc3_omap_writel(omap->base, USBOTGSS_IRQENABLE_SET_1, reg);
-
- if (node) {
- ret = of_platform_populate(node, NULL, NULL, dev);
- if (ret) {
- dev_err(&pdev->dev,
- "failed to add create dwc3 core\n");
- return ret;
- }
+ ret = of_platform_populate(node, NULL, NULL, dev);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to create dwc3 core\n");
+ return ret;
}
return 0;
@@ -454,8 +394,7 @@ static int dwc3_omap_remove(struct platform_device *pdev)
{
struct dwc3_omap *omap = platform_get_drvdata(pdev);
- platform_device_unregister(omap->usb2_phy);
- platform_device_unregister(omap->usb3_phy);
+ dwc3_omap_disable_irqs(omap);
pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
device_for_each_child(&pdev->dev, NULL, dwc3_omap_remove_core);
@@ -465,18 +404,72 @@ static int dwc3_omap_remove(struct platform_device *pdev)
static const struct of_device_id of_dwc3_match[] = {
{
- "ti,dwc3",
+ .compatible = "ti,dwc3"
},
{ },
};
MODULE_DEVICE_TABLE(of, of_dwc3_match);
+#ifdef CONFIG_PM_SLEEP
+static int dwc3_omap_prepare(struct device *dev)
+{
+ struct dwc3_omap *omap = dev_get_drvdata(dev);
+
+ dwc3_omap_disable_irqs(omap);
+
+ return 0;
+}
+
+static void dwc3_omap_complete(struct device *dev)
+{
+ struct dwc3_omap *omap = dev_get_drvdata(dev);
+
+ dwc3_omap_enable_irqs(omap);
+}
+
+static int dwc3_omap_suspend(struct device *dev)
+{
+ struct dwc3_omap *omap = dev_get_drvdata(dev);
+
+ omap->utmi_otg_status = dwc3_omap_readl(omap->base,
+ USBOTGSS_UTMI_OTG_STATUS);
+
+ return 0;
+}
+
+static int dwc3_omap_resume(struct device *dev)
+{
+ struct dwc3_omap *omap = dev_get_drvdata(dev);
+
+ dwc3_omap_writel(omap->base, USBOTGSS_UTMI_OTG_STATUS,
+ omap->utmi_otg_status);
+
+ pm_runtime_disable(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+
+ return 0;
+}
+
+static const struct dev_pm_ops dwc3_omap_dev_pm_ops = {
+ .prepare = dwc3_omap_prepare,
+ .complete = dwc3_omap_complete,
+
+ SET_SYSTEM_SLEEP_PM_OPS(dwc3_omap_suspend, dwc3_omap_resume)
+};
+
+#define DEV_PM_OPS (&dwc3_omap_dev_pm_ops)
+#else
+#define DEV_PM_OPS NULL
+#endif /* CONFIG_PM_SLEEP */
+
static struct platform_driver dwc3_omap_driver = {
.probe = dwc3_omap_probe,
.remove = dwc3_omap_remove,
.driver = {
.name = "omap-dwc3",
.of_match_table = of_dwc3_match,
+ .pm = DEV_PM_OPS,
},
};
diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c
index e8d77689a32..227d4a7acad 100644
--- a/drivers/usb/dwc3/dwc3-pci.c
+++ b/drivers/usb/dwc3/dwc3-pci.c
@@ -212,11 +212,49 @@ static DEFINE_PCI_DEVICE_TABLE(dwc3_pci_id_table) = {
};
MODULE_DEVICE_TABLE(pci, dwc3_pci_id_table);
+#ifdef CONFIG_PM
+static int dwc3_pci_suspend(struct device *dev)
+{
+ struct pci_dev *pci = to_pci_dev(dev);
+
+ pci_disable_device(pci);
+
+ return 0;
+}
+
+static int dwc3_pci_resume(struct device *dev)
+{
+ struct pci_dev *pci = to_pci_dev(dev);
+ int ret;
+
+ ret = pci_enable_device(pci);
+ if (ret) {
+ dev_err(dev, "can't re-enable device --> %d\n", ret);
+ return ret;
+ }
+
+ pci_set_master(pci);
+
+ return 0;
+}
+
+static const struct dev_pm_ops dwc3_pci_dev_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(dwc3_pci_suspend, dwc3_pci_resume)
+};
+
+#define DEV_PM_OPS (&dwc3_pci_dev_pm_ops)
+#else
+#define DEV_PM_OPS NULL
+#endif /* CONFIG_PM */
+
static struct pci_driver dwc3_pci_driver = {
.name = "dwc3-pci",
.id_table = dwc3_pci_id_table,
.probe = dwc3_pci_probe,
.remove = dwc3_pci_remove,
+ .driver = {
+ .pm = DEV_PM_OPS,
+ },
};
MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>");
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index 1d139ca05ef..5acbb948b70 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -394,10 +394,13 @@ static int dwc3_ep0_handle_feature(struct dwc3 *dwc,
u32 wIndex;
u32 reg;
int ret;
+ enum usb_device_state state;
wValue = le16_to_cpu(ctrl->wValue);
wIndex = le16_to_cpu(ctrl->wIndex);
recip = ctrl->bRequestType & USB_RECIP_MASK;
+ state = dwc->gadget.state;
+
switch (recip) {
case USB_RECIP_DEVICE:
@@ -409,7 +412,7 @@ static int dwc3_ep0_handle_feature(struct dwc3 *dwc,
* default control pipe
*/
case USB_DEVICE_U1_ENABLE:
- if (dwc->dev_state != DWC3_CONFIGURED_STATE)
+ if (state != USB_STATE_CONFIGURED)
return -EINVAL;
if (dwc->speed != DWC3_DSTS_SUPERSPEED)
return -EINVAL;
@@ -423,7 +426,7 @@ static int dwc3_ep0_handle_feature(struct dwc3 *dwc,
break;
case USB_DEVICE_U2_ENABLE:
- if (dwc->dev_state != DWC3_CONFIGURED_STATE)
+ if (state != USB_STATE_CONFIGURED)
return -EINVAL;
if (dwc->speed != DWC3_DSTS_SUPERSPEED)
return -EINVAL;
@@ -493,6 +496,7 @@ 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;
u32 addr;
u32 reg;
@@ -502,7 +506,7 @@ static int dwc3_ep0_set_address(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
return -EINVAL;
}
- if (dwc->dev_state == DWC3_CONFIGURED_STATE) {
+ if (state == USB_STATE_CONFIGURED) {
dev_dbg(dwc->dev, "trying to set address when configured\n");
return -EINVAL;
}
@@ -513,9 +517,9 @@ static int dwc3_ep0_set_address(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
dwc3_writel(dwc->regs, DWC3_DCFG, reg);
if (addr)
- dwc->dev_state = DWC3_ADDRESS_STATE;
+ usb_gadget_set_state(&dwc->gadget, USB_STATE_ADDRESS);
else
- dwc->dev_state = DWC3_DEFAULT_STATE;
+ usb_gadget_set_state(&dwc->gadget, USB_STATE_DEFAULT);
return 0;
}
@@ -532,6 +536,7 @@ static int dwc3_ep0_delegate_req(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
{
+ enum usb_device_state state = dwc->gadget.state;
u32 cfg;
int ret;
u32 reg;
@@ -539,16 +544,18 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
dwc->start_config_issued = false;
cfg = le16_to_cpu(ctrl->wValue);
- switch (dwc->dev_state) {
- case DWC3_DEFAULT_STATE:
+ switch (state) {
+ case USB_STATE_DEFAULT:
return -EINVAL;
break;
- case DWC3_ADDRESS_STATE:
+ case USB_STATE_ADDRESS:
ret = dwc3_ep0_delegate_req(dwc, ctrl);
/* if the cfg matches and the cfg is non zero */
if (cfg && (!ret || (ret == USB_GADGET_DELAYED_STATUS))) {
- dwc->dev_state = DWC3_CONFIGURED_STATE;
+ usb_gadget_set_state(&dwc->gadget,
+ USB_STATE_CONFIGURED);
+
/*
* Enable transition to U1/U2 state when
* nothing is pending from application.
@@ -562,10 +569,11 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
}
break;
- case DWC3_CONFIGURED_STATE:
+ case USB_STATE_CONFIGURED:
ret = dwc3_ep0_delegate_req(dwc, ctrl);
if (!cfg)
- dwc->dev_state = DWC3_ADDRESS_STATE;
+ usb_gadget_set_state(&dwc->gadget,
+ USB_STATE_ADDRESS);
break;
default:
ret = -EINVAL;
@@ -620,10 +628,11 @@ 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;
u16 wLength;
u16 wValue;
- if (dwc->dev_state == DWC3_DEFAULT_STATE)
+ if (state == USB_STATE_DEFAULT)
return -EINVAL;
wValue = le16_to_cpu(ctrl->wValue);
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 82e160e96fc..2b6e7e00120 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -1425,8 +1425,10 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on)
if (dwc->revision >= DWC3_REVISION_194A)
reg &= ~DWC3_DCTL_KEEP_CONNECT;
reg |= DWC3_DCTL_RUN_STOP;
+ dwc->pullups_connected = true;
} else {
reg &= ~DWC3_DCTL_RUN_STOP;
+ dwc->pullups_connected = false;
}
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
@@ -1469,6 +1471,33 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
return ret;
}
+static void dwc3_gadget_enable_irq(struct dwc3 *dwc)
+{
+ u32 reg;
+
+ /* Enable all but Start and End of Frame IRQs */
+ reg = (DWC3_DEVTEN_VNDRDEVTSTRCVEDEN |
+ DWC3_DEVTEN_EVNTOVERFLOWEN |
+ DWC3_DEVTEN_CMDCMPLTEN |
+ DWC3_DEVTEN_ERRTICERREN |
+ DWC3_DEVTEN_WKUPEVTEN |
+ DWC3_DEVTEN_ULSTCNGEN |
+ DWC3_DEVTEN_CONNECTDONEEN |
+ DWC3_DEVTEN_USBRSTEN |
+ DWC3_DEVTEN_DISCONNEVTEN);
+
+ dwc3_writel(dwc->regs, DWC3_DEVTEN, reg);
+}
+
+static void dwc3_gadget_disable_irq(struct dwc3 *dwc)
+{
+ /* mask all interrupts */
+ dwc3_writel(dwc->regs, DWC3_DEVTEN, 0x00);
+}
+
+static irqreturn_t dwc3_interrupt(int irq, void *_dwc);
+static irqreturn_t dwc3_thread_interrupt(int irq, void *_dwc);
+
static int dwc3_gadget_start(struct usb_gadget *g,
struct usb_gadget_driver *driver)
{
@@ -1476,6 +1505,7 @@ static int dwc3_gadget_start(struct usb_gadget *g,
struct dwc3_ep *dep;
unsigned long flags;
int ret = 0;
+ int irq;
u32 reg;
spin_lock_irqsave(&dwc->lock, flags);
@@ -1489,7 +1519,6 @@ static int dwc3_gadget_start(struct usb_gadget *g,
}
dwc->gadget_driver = driver;
- dwc->gadget.dev.driver = &driver->driver;
reg = dwc3_readl(dwc->regs, DWC3_DCFG);
reg &= ~(DWC3_DCFG_SPEED_MASK);
@@ -1536,6 +1565,17 @@ static int dwc3_gadget_start(struct usb_gadget *g,
dwc->ep0state = EP0_SETUP_PHASE;
dwc3_ep0_out_start(dwc);
+ irq = platform_get_irq(to_platform_device(dwc->dev), 0);
+ ret = request_threaded_irq(irq, dwc3_interrupt, dwc3_thread_interrupt,
+ IRQF_SHARED | IRQF_ONESHOT, "dwc3", dwc);
+ if (ret) {
+ dev_err(dwc->dev, "failed to request irq #%d --> %d\n",
+ irq, ret);
+ goto err1;
+ }
+
+ dwc3_gadget_enable_irq(dwc);
+
spin_unlock_irqrestore(&dwc->lock, flags);
return 0;
@@ -1554,14 +1594,18 @@ static int dwc3_gadget_stop(struct usb_gadget *g,
{
struct dwc3 *dwc = gadget_to_dwc(g);
unsigned long flags;
+ int irq;
spin_lock_irqsave(&dwc->lock, flags);
+ dwc3_gadget_disable_irq(dwc);
+ irq = platform_get_irq(to_platform_device(dwc->dev), 0);
+ free_irq(irq, dwc);
+
__dwc3_gadget_ep_disable(dwc->eps[0]);
__dwc3_gadget_ep_disable(dwc->eps[1]);
dwc->gadget_driver = NULL;
- dwc->gadget.dev.driver = NULL;
spin_unlock_irqrestore(&dwc->lock, flags);
@@ -1579,14 +1623,15 @@ static const struct usb_gadget_ops dwc3_gadget_ops = {
/* -------------------------------------------------------------------------- */
-static int dwc3_gadget_init_endpoints(struct dwc3 *dwc)
+static int dwc3_gadget_init_hw_endpoints(struct dwc3 *dwc,
+ u8 num, u32 direction)
{
struct dwc3_ep *dep;
- u8 epnum;
+ u8 i;
- INIT_LIST_HEAD(&dwc->gadget.ep_list);
+ for (i = 0; i < num; i++) {
+ u8 epnum = (i << 1) | (!!direction);
- for (epnum = 0; epnum < DWC3_ENDPOINTS_NUM; epnum++) {
dep = kzalloc(sizeof(*dep), GFP_KERNEL);
if (!dep) {
dev_err(dwc->dev, "can't allocate endpoint %d\n",
@@ -1600,6 +1645,7 @@ static int dwc3_gadget_init_endpoints(struct dwc3 *dwc)
snprintf(dep->name, sizeof(dep->name), "ep%d%s", epnum >> 1,
(epnum & 1) ? "in" : "out");
+
dep->endpoint.name = dep->name;
dep->direction = (epnum & 1);
@@ -1630,6 +1676,27 @@ static int dwc3_gadget_init_endpoints(struct dwc3 *dwc)
return 0;
}
+static int dwc3_gadget_init_endpoints(struct dwc3 *dwc)
+{
+ int ret;
+
+ INIT_LIST_HEAD(&dwc->gadget.ep_list);
+
+ ret = dwc3_gadget_init_hw_endpoints(dwc, dwc->num_out_eps, 0);
+ if (ret < 0) {
+ dev_vdbg(dwc->dev, "failed to allocate OUT endpoints\n");
+ return ret;
+ }
+
+ ret = dwc3_gadget_init_hw_endpoints(dwc, dwc->num_in_eps, 1);
+ if (ret < 0) {
+ dev_vdbg(dwc->dev, "failed to allocate IN endpoints\n");
+ return ret;
+ }
+
+ return 0;
+}
+
static void dwc3_gadget_free_endpoints(struct dwc3 *dwc)
{
struct dwc3_ep *dep;
@@ -1637,6 +1704,9 @@ static void dwc3_gadget_free_endpoints(struct dwc3 *dwc)
for (epnum = 0; epnum < DWC3_ENDPOINTS_NUM; epnum++) {
dep = dwc->eps[epnum];
+ if (!dep)
+ continue;
+
dwc3_free_trb_pool(dep);
if (epnum != 0 && epnum != 1)
@@ -1646,12 +1716,8 @@ static void dwc3_gadget_free_endpoints(struct dwc3 *dwc)
}
}
-static void dwc3_gadget_release(struct device *dev)
-{
- dev_dbg(dev, "%s\n", __func__);
-}
-
/* -------------------------------------------------------------------------- */
+
static int __dwc3_cleanup_done_trbs(struct dwc3 *dwc, struct dwc3_ep *dep,
struct dwc3_request *req, struct dwc3_trb *trb,
const struct dwc3_event_depevt *event, int status)
@@ -1975,6 +2041,9 @@ static void dwc3_stop_active_transfers(struct dwc3 *dwc)
struct dwc3_ep *dep;
dep = dwc->eps[epnum];
+ if (!dep)
+ continue;
+
if (!(dep->flags & DWC3_EP_ENABLED))
continue;
@@ -1992,6 +2061,8 @@ static void dwc3_clear_stall_all_ep(struct dwc3 *dwc)
int ret;
dep = dwc->eps[epnum];
+ if (!dep)
+ continue;
if (!(dep->flags & DWC3_EP_STALL))
continue;
@@ -2091,7 +2162,7 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
}
/* after reset -> Default State */
- dwc->dev_state = DWC3_DEFAULT_STATE;
+ usb_gadget_set_state(&dwc->gadget, USB_STATE_DEFAULT);
/* Recent versions support automatic phy suspend and don't need this */
if (dwc->revision < DWC3_REVISION_194A) {
@@ -2277,6 +2348,34 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc,
unsigned int evtinfo)
{
enum dwc3_link_state next = evtinfo & DWC3_LINK_STATE_MASK;
+ unsigned int pwropt;
+
+ /*
+ * WORKAROUND: DWC3 < 2.50a have an issue when configured without
+ * Hibernation mode enabled which would show up when device detects
+ * host-initiated U3 exit.
+ *
+ * In that case, device will generate a Link State Change Interrupt
+ * from U3 to RESUME which is only necessary if Hibernation is
+ * configured in.
+ *
+ * There are no functional changes due to such spurious event and we
+ * just need to ignore it.
+ *
+ * Refers to:
+ *
+ * STAR#9000570034 RTL: SS Resume event generated in non-Hibernation
+ * operational mode
+ */
+ pwropt = DWC3_GHWPARAMS1_EN_PWROPT(dwc->hwparams.hwparams1);
+ if ((dwc->revision < DWC3_REVISION_250A) &&
+ (pwropt != DWC3_GHWPARAMS1_EN_PWROPT_HIB)) {
+ if ((dwc->link_state == DWC3_LINK_STATE_U3) &&
+ (next == DWC3_LINK_STATE_RESUME)) {
+ dev_vdbg(dwc->dev, "ignoring transition U3 -> Resume\n");
+ return;
+ }
+ }
/*
* WORKAROUND: DWC3 Revisions <1.83a have an issue which, depending
@@ -2387,40 +2486,73 @@ static void dwc3_process_event_entry(struct dwc3 *dwc,
}
}
+static irqreturn_t dwc3_thread_interrupt(int irq, void *_dwc)
+{
+ struct dwc3 *dwc = _dwc;
+ unsigned long flags;
+ irqreturn_t ret = IRQ_NONE;
+ int i;
+
+ spin_lock_irqsave(&dwc->lock, flags);
+
+ for (i = 0; i < dwc->num_event_buffers; i++) {
+ struct dwc3_event_buffer *evt;
+ int left;
+
+ evt = dwc->ev_buffs[i];
+ left = evt->count;
+
+ if (!(evt->flags & DWC3_EVENT_PENDING))
+ continue;
+
+ while (left > 0) {
+ union dwc3_event event;
+
+ event.raw = *(u32 *) (evt->buf + evt->lpos);
+
+ dwc3_process_event_entry(dwc, &event);
+
+ /*
+ * FIXME we wrap around correctly to the next entry as
+ * almost all entries are 4 bytes in size. There is one
+ * entry which has 12 bytes which is a regular entry
+ * followed by 8 bytes data. ATM I don't know how
+ * things are organized if we get next to the a
+ * boundary so I worry about that once we try to handle
+ * that.
+ */
+ evt->lpos = (evt->lpos + 4) % DWC3_EVENT_BUFFERS_SIZE;
+ left -= 4;
+
+ dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(i), 4);
+ }
+
+ evt->count = 0;
+ evt->flags &= ~DWC3_EVENT_PENDING;
+ ret = IRQ_HANDLED;
+ }
+
+ spin_unlock_irqrestore(&dwc->lock, flags);
+
+ return ret;
+}
+
static irqreturn_t dwc3_process_event_buf(struct dwc3 *dwc, u32 buf)
{
struct dwc3_event_buffer *evt;
- int left;
u32 count;
+ evt = dwc->ev_buffs[buf];
+
count = dwc3_readl(dwc->regs, DWC3_GEVNTCOUNT(buf));
count &= DWC3_GEVNTCOUNT_MASK;
if (!count)
return IRQ_NONE;
- evt = dwc->ev_buffs[buf];
- left = count;
-
- while (left > 0) {
- union dwc3_event event;
-
- event.raw = *(u32 *) (evt->buf + evt->lpos);
+ evt->count = count;
+ evt->flags |= DWC3_EVENT_PENDING;
- dwc3_process_event_entry(dwc, &event);
- /*
- * XXX we wrap around correctly to the next entry as almost all
- * entries are 4 bytes in size. There is one entry which has 12
- * bytes which is a regular entry followed by 8 bytes data. ATM
- * I don't know how things are organized if were get next to the
- * a boundary so I worry about that once we try to handle that.
- */
- evt->lpos = (evt->lpos + 4) % DWC3_EVENT_BUFFERS_SIZE;
- left -= 4;
-
- dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(buf), 4);
- }
-
- return IRQ_HANDLED;
+ return IRQ_WAKE_THREAD;
}
static irqreturn_t dwc3_interrupt(int irq, void *_dwc)
@@ -2435,7 +2567,7 @@ static irqreturn_t dwc3_interrupt(int irq, void *_dwc)
irqreturn_t status;
status = dwc3_process_event_buf(dwc, i);
- if (status == IRQ_HANDLED)
+ if (status == IRQ_WAKE_THREAD)
ret = status;
}
@@ -2454,7 +2586,6 @@ int dwc3_gadget_init(struct dwc3 *dwc)
{
u32 reg;
int ret;
- int irq;
dwc->ctrl_req = dma_alloc_coherent(dwc->dev, sizeof(*dwc->ctrl_req),
&dwc->ctrl_req_addr, GFP_KERNEL);
@@ -2488,19 +2619,10 @@ int dwc3_gadget_init(struct dwc3 *dwc)
goto err3;
}
- dev_set_name(&dwc->gadget.dev, "gadget");
-
dwc->gadget.ops = &dwc3_gadget_ops;
dwc->gadget.max_speed = USB_SPEED_SUPER;
dwc->gadget.speed = USB_SPEED_UNKNOWN;
- dwc->gadget.dev.parent = dwc->dev;
dwc->gadget.sg_supported = true;
-
- dma_set_coherent_mask(&dwc->gadget.dev, dwc->dev->coherent_dma_mask);
-
- dwc->gadget.dev.dma_parms = dwc->dev->dma_parms;
- dwc->gadget.dev.dma_mask = dwc->dev->dma_mask;
- dwc->gadget.dev.release = dwc3_gadget_release;
dwc->gadget.name = "dwc3-gadget";
/*
@@ -2512,60 +2634,24 @@ int dwc3_gadget_init(struct dwc3 *dwc)
if (ret)
goto err4;
- irq = platform_get_irq(to_platform_device(dwc->dev), 0);
-
- ret = request_irq(irq, dwc3_interrupt, IRQF_SHARED,
- "dwc3", dwc);
- if (ret) {
- dev_err(dwc->dev, "failed to request irq #%d --> %d\n",
- irq, ret);
- goto err5;
- }
-
reg = dwc3_readl(dwc->regs, DWC3_DCFG);
reg |= DWC3_DCFG_LPM_CAP;
dwc3_writel(dwc->regs, DWC3_DCFG, reg);
- /* Enable all but Start and End of Frame IRQs */
- reg = (DWC3_DEVTEN_VNDRDEVTSTRCVEDEN |
- DWC3_DEVTEN_EVNTOVERFLOWEN |
- DWC3_DEVTEN_CMDCMPLTEN |
- DWC3_DEVTEN_ERRTICERREN |
- DWC3_DEVTEN_WKUPEVTEN |
- DWC3_DEVTEN_ULSTCNGEN |
- DWC3_DEVTEN_CONNECTDONEEN |
- DWC3_DEVTEN_USBRSTEN |
- DWC3_DEVTEN_DISCONNEVTEN);
- dwc3_writel(dwc->regs, DWC3_DEVTEN, reg);
-
- /* automatic phy suspend only on recent versions */
+ /* Enable USB2 LPM and automatic phy suspend only on recent versions */
if (dwc->revision >= DWC3_REVISION_194A) {
dwc3_gadget_usb2_phy_suspend(dwc, false);
dwc3_gadget_usb3_phy_suspend(dwc, false);
}
- ret = device_register(&dwc->gadget.dev);
- if (ret) {
- dev_err(dwc->dev, "failed to register gadget device\n");
- put_device(&dwc->gadget.dev);
- goto err6;
- }
-
ret = usb_add_gadget_udc(dwc->dev, &dwc->gadget);
if (ret) {
dev_err(dwc->dev, "failed to register udc\n");
- goto err7;
+ goto err5;
}
return 0;
-err7:
- device_unregister(&dwc->gadget.dev);
-
-err6:
- dwc3_writel(dwc->regs, DWC3_DEVTEN, 0x00);
- free_irq(irq, dwc);
-
err5:
dwc3_gadget_free_endpoints(dwc);
@@ -2588,15 +2674,11 @@ err0:
return ret;
}
+/* -------------------------------------------------------------------------- */
+
void dwc3_gadget_exit(struct dwc3 *dwc)
{
- int irq;
-
usb_del_gadget_udc(&dwc->gadget);
- irq = platform_get_irq(to_platform_device(dwc->dev), 0);
-
- dwc3_writel(dwc->regs, DWC3_DEVTEN, 0x00);
- free_irq(irq, dwc);
dwc3_gadget_free_endpoints(dwc);
@@ -2610,6 +2692,63 @@ void dwc3_gadget_exit(struct dwc3 *dwc)
dma_free_coherent(dwc->dev, sizeof(*dwc->ctrl_req),
dwc->ctrl_req, dwc->ctrl_req_addr);
+}
- device_unregister(&dwc->gadget.dev);
+int dwc3_gadget_prepare(struct dwc3 *dwc)
+{
+ if (dwc->pullups_connected)
+ dwc3_gadget_disable_irq(dwc);
+
+ return 0;
+}
+
+void dwc3_gadget_complete(struct dwc3 *dwc)
+{
+ if (dwc->pullups_connected) {
+ dwc3_gadget_enable_irq(dwc);
+ dwc3_gadget_run_stop(dwc, true);
+ }
+}
+
+int dwc3_gadget_suspend(struct dwc3 *dwc)
+{
+ __dwc3_gadget_ep_disable(dwc->eps[0]);
+ __dwc3_gadget_ep_disable(dwc->eps[1]);
+
+ dwc->dcfg = dwc3_readl(dwc->regs, DWC3_DCFG);
+
+ return 0;
+}
+
+int dwc3_gadget_resume(struct dwc3 *dwc)
+{
+ struct dwc3_ep *dep;
+ int ret;
+
+ /* Start with SuperSpeed Default */
+ dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512);
+
+ dep = dwc->eps[0];
+ ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false);
+ if (ret)
+ goto err0;
+
+ dep = dwc->eps[1];
+ ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false);
+ if (ret)
+ goto err1;
+
+ /* begin to receive SETUP packets */
+ dwc->ep0state = EP0_SETUP_PHASE;
+ dwc3_ep0_out_start(dwc);
+
+ dwc3_writel(dwc->regs, DWC3_DCFG, dwc->dcfg);
+
+ return 0;
+
+err1:
+ __dwc3_gadget_ep_disable(dwc->eps[0]);
+
+err0:
+ return ret;
}