USB: dwc3: Add support for fixing superspeed enumeration issue

Setting SSPHY SUSP bit (bit 17) in GUSB3PIPECTL(0) register
might cause device enumerating at high speed mode instead of
superspeed mode on some platforms. Hence add workaround by
clearing the SSPHY SUSP bit during disconnect and setting it
after it is configured to fix this enumeration issue on those
platforms.

Also add support for disabling U1 and U2 low power modes  which
could also affect this enumeration issue.

CRs-Fixed: 637902
Change-Id: I8668ced09a88b77f37265ab15e89fa9e964bfbe9
Signed-off-by: Vijayavardhan Vennapusa <vvreddy@codeaurora.org>
[jackp@codeaurora.org: only add u1/u2 disable bits]
Signed-off-by: Jack Pham <jackp@codeaurora.org>
This commit is contained in:
Vijayavardhan Vennapusa 2014-05-08 13:47:50 +05:30 committed by David Keitel
parent 01edad63c9
commit 567c83ebc7
4 changed files with 29 additions and 7 deletions

View file

@ -48,6 +48,8 @@ Optional properties:
fladj_30mhz_sdbnd signal is invalid or incorrect. fladj_30mhz_sdbnd signal is invalid or incorrect.
- snps,nominal-elastic-buffer: When set, the nominal elastic buffer setting - snps,nominal-elastic-buffer: When set, the nominal elastic buffer setting
is used. By default, the half-full setting is used. is used. By default, the half-full setting is used.
- snps,usb3-u1u2-disable: If present, disable u1u2 low power modes for DWC3 core
controller in SS mode.
This is usually a subnode to DWC3 glue to which it is connected. This is usually a subnode to DWC3 glue to which it is connected.
@ -57,4 +59,5 @@ dwc3@4a030000 {
interrupts = <0 92 4> interrupts = <0 92 4>
usb-phy = <&usb2_phy>, <&usb3,phy>; usb-phy = <&usb2_phy>, <&usb3,phy>;
tx-fifo-resize; tx-fifo-resize;
snps,usb3-u1u2-disable;
}; };

View file

@ -997,6 +997,8 @@ static int dwc3_probe(struct platform_device *pdev)
dwc->nominal_elastic_buffer = device_property_read_bool(dev, dwc->nominal_elastic_buffer = device_property_read_bool(dev,
"snps,nominal-elastic-buffer"); "snps,nominal-elastic-buffer");
dwc->usb3_u1u2_disable = device_property_read_bool(dev,
"snps,usb3-u1u2-disable");
if (pdata) { if (pdata) {
dwc->maximum_speed = pdata->maximum_speed; dwc->maximum_speed = pdata->maximum_speed;

View file

@ -797,6 +797,7 @@ struct dwc3_scratchpad_array {
* 2 - No de-emphasis * 2 - No de-emphasis
* 3 - Reserved * 3 - Reserved
* @err_evt_seen: previous event in queue was erratic error * @err_evt_seen: previous event in queue was erratic error
* @usb3_u1u2_disable: if true, disable U1U2 low power modes in Superspeed mode.
* @in_lpm: indicates if controller is in low power mode (no clocks) * @in_lpm: indicates if controller is in low power mode (no clocks)
*/ */
struct dwc3 { struct dwc3 {
@ -945,6 +946,7 @@ struct dwc3 {
unsigned nominal_elastic_buffer:1; unsigned nominal_elastic_buffer:1;
unsigned err_evt_seen:1; unsigned err_evt_seen:1;
unsigned usb3_u1u2_disable:1;
struct dwc3_gadget_events dbg_gadget_events; struct dwc3_gadget_events dbg_gadget_events;

View file

@ -16,6 +16,7 @@
* GNU General Public License for more details. * GNU General Public License for more details.
*/ */
#include <linux/module.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
@ -36,6 +37,11 @@
#include "io.h" #include "io.h"
#include "debug.h" #include "debug.h"
static bool enable_dwc3_u1u2;
module_param(enable_dwc3_u1u2, bool, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(enable_dwc3_u1u2, "Enable support for U1U2 low power modes");
static void __dwc3_ep0_do_control_status(struct dwc3 *dwc, struct dwc3_ep *dep); static void __dwc3_ep0_do_control_status(struct dwc3 *dwc, struct dwc3_ep *dep);
static void __dwc3_ep0_do_control_data(struct dwc3 *dwc, static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
struct dwc3_ep *dep, struct dwc3_request *req); struct dwc3_ep *dep, struct dwc3_request *req);
@ -431,6 +437,9 @@ static int dwc3_ep0_handle_feature(struct dwc3 *dwc,
if (dwc->speed != DWC3_DSTS_SUPERSPEED) if (dwc->speed != DWC3_DSTS_SUPERSPEED)
return -EINVAL; return -EINVAL;
if (dwc->usb3_u1u2_disable && !enable_dwc3_u1u2)
return -EINVAL;
reg = dwc3_readl(dwc->regs, DWC3_DCTL); reg = dwc3_readl(dwc->regs, DWC3_DCTL);
if (set) if (set)
reg |= DWC3_DCTL_INITU1ENA; reg |= DWC3_DCTL_INITU1ENA;
@ -445,6 +454,9 @@ static int dwc3_ep0_handle_feature(struct dwc3 *dwc,
if (dwc->speed != DWC3_DSTS_SUPERSPEED) if (dwc->speed != DWC3_DSTS_SUPERSPEED)
return -EINVAL; return -EINVAL;
if (dwc->usb3_u1u2_disable && !enable_dwc3_u1u2)
return -EINVAL;
reg = dwc3_readl(dwc->regs, DWC3_DCTL); reg = dwc3_readl(dwc->regs, DWC3_DCTL);
if (set) if (set)
reg |= DWC3_DCTL_INITU2ENA; reg |= DWC3_DCTL_INITU2ENA;
@ -579,13 +591,16 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
usb_gadget_set_state(&dwc->gadget, usb_gadget_set_state(&dwc->gadget,
USB_STATE_CONFIGURED); USB_STATE_CONFIGURED);
/* if (!dwc->usb3_u1u2_disable || enable_dwc3_u1u2) {
* Enable transition to U1/U2 state when /*
* nothing is pending from application. * Enable transition to U1/U2 state when
*/ * nothing is pending from application.
reg = dwc3_readl(dwc->regs, DWC3_DCTL); */
reg |= (DWC3_DCTL_ACCEPTU1ENA | DWC3_DCTL_ACCEPTU2ENA); reg = dwc3_readl(dwc->regs, DWC3_DCTL);
dwc3_writel(dwc->regs, DWC3_DCTL, reg); reg |= (DWC3_DCTL_ACCEPTU1ENA |
DWC3_DCTL_ACCEPTU2ENA);
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
}
dwc->resize_fifos = true; dwc->resize_fifos = true;
dwc3_trace(trace_dwc3_ep0, "resize FIFOs flag SET"); dwc3_trace(trace_dwc3_ep0, "resize FIFOs flag SET");