* dwc3_soft_reset - Issue soft reset
* @dwc: Pointer to our controller context structure
*/
-static int dwc3_soft_reset(struct dwc3 *dwc)
+int dwc3_soft_reset(struct dwc3 *dwc)
{
unsigned long timeout;
u32 reg;
*
* Returns 0 on success otherwise negative errno.
*/
-static int dwc3_event_buffers_setup(struct dwc3 *dwc)
+int dwc3_event_buffers_setup(struct dwc3 *dwc)
{
struct dwc3_event_buffer *evt;
int n;
return 0;
}
-static void dwc3_event_buffers_cleanup(struct dwc3 *dwc)
+void dwc3_event_buffers_cleanup(struct dwc3 *dwc)
{
struct dwc3_event_buffer *evt;
int n;
}
}
+/* Returns true if the controller is capable of DRD. */
+bool dwc3_hw_is_drd(struct dwc3 *dwc)
+{
+ u32 op_mode = DWC3_GHWPARAMS0_USB3_MODE(dwc->hwparams.hwparams0);
+
+ return (op_mode == DWC3_GHWPARAMS0_USB3_DRD);
+}
+
+bool dwc3_force_mode(struct dwc3 *dwc, u32 mode)
+{
+ u32 reg;
+ unsigned long flags;
+ struct usb_hcd *hcd;
+
+ /*
+ * Force mode has no effect if the hardware is not drd mode.
+ */
+ if (!dwc3_hw_is_drd(dwc))
+ return false;
+ /*
+ * If dr_mode is either peripheral or host only, there is no
+ * need to ever force the mode to the opposite mode.
+ */
+ if (WARN_ON(mode == DWC3_GCTL_PRTCAP_DEVICE &&
+ dwc->dr_mode == USB_DR_MODE_HOST))
+ return false;
+
+ if (WARN_ON(mode == DWC3_GCTL_PRTCAP_HOST &&
+ dwc->dr_mode == USB_DR_MODE_PERIPHERAL))
+ return false;
+
+ reg = dwc3_readl(dwc->regs, DWC3_GCTL);
+ if (DWC3_GCTL_PRTCAP(reg) == mode)
+ return false;
+
+ hcd = dev_get_drvdata(&dwc->xhci->dev);
+
+ switch (mode) {
+ case DWC3_GCTL_PRTCAP_DEVICE:
+ if (hcd->state != HC_STATE_HALT) {
+ usb_remove_hcd(hcd->shared_hcd);
+ usb_remove_hcd(hcd);
+ }
+
+ spin_lock_irqsave(&dwc->lock, flags);
+ dwc3_set_mode(dwc, mode);
+ dwc3_gadget_restart(dwc, true);
+ spin_unlock_irqrestore(&dwc->lock, flags);
+
+ break;
+ case DWC3_GCTL_PRTCAP_HOST:
+ spin_lock_irqsave(&dwc->lock, flags);
+ dwc3_gadget_restart(dwc, false);
+ dwc3_set_mode(dwc, mode);
+ spin_unlock_irqrestore(&dwc->lock, flags);
+
+ if (hcd->state == HC_STATE_HALT) {
+ usb_add_hcd(hcd, hcd->irq, IRQF_SHARED);
+ usb_add_hcd(hcd->shared_hcd, hcd->irq, IRQF_SHARED);
+ }
+
+ break;
+ default:
+ /* do nothing */
+ break;
+ }
+
+ return true;
+}
+
#define DWC3_ALIGN_MASK (16 - 1)
static int dwc3_probe(struct platform_device *pdev)
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
#include <linux/usb/otg.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+#include <linux/platform_device.h>
+#include <linux/version.h>
#include <linux/ulpi/interface.h>
#include <linux/phy/phy.h>
#define DWC3_GEVNTSIZ_INTMASK (1 << 31)
#define DWC3_GEVNTSIZ_SIZE(n) ((n) & 0xffff)
+/* Global HWPARAMS0 Register */
+#define DWC3_GHWPARAMS0_USB3_MODE(n) ((n) & 7)
+#define DWC3_GHWPARAMS0_USB3_DRD 2
+
/* Global HWPARAMS1 Register */
#define DWC3_GHWPARAMS1_EN_PWROPT(n) (((n) & (3 << 24)) >> 24)
#define DWC3_GHWPARAMS1_EN_PWROPT_NO 0
* 1 - utmi_l1_suspend_n
* @is_fpga: true when we are using the FPGA board
* @needs_fifo_resize: not all users might want fifo resizing, flag it
+ * @enabled: true when gadget driver is ready
* @pullups_connected: true when Run/Stop bit is set
* @resize_fifos: tells us it's ok to reconfigure our TxFIFO sizes.
* @setup_packet_pending: true when there's a Setup Packet in FIFO. Workaround
unsigned is_utmi_l1_suspend:1;
unsigned is_fpga:1;
unsigned needs_fifo_resize:1;
+ unsigned enabled:1;
unsigned pullups_connected:1;
unsigned resize_fifos:1;
unsigned setup_packet_pending:1;
/* prototypes */
void dwc3_set_mode(struct dwc3 *dwc, u32 mode);
int dwc3_gadget_resize_tx_fifos(struct dwc3 *dwc);
+int dwc3_soft_reset(struct dwc3 *dwc);
+int dwc3_event_buffers_setup(struct dwc3 *dwc);
+void dwc3_event_buffers_cleanup(struct dwc3 *dwc);
+int dwc3_gadget_restart(struct dwc3 *dwc, bool start);
+bool dwc3_force_mode(struct dwc3 *dwc, u32 mode);
#if IS_ENABLED(CONFIG_USB_DWC3_HOST) || IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)
int dwc3_host_init(struct dwc3 *dwc);
{
struct dwc3 *dwc = gadget_to_dwc(g);
unsigned long flags;
- int ret;
+ int ret = 0;
+ u32 reg;
is_on = !!is_on;
spin_lock_irqsave(&dwc->lock, flags);
- ret = dwc3_gadget_run_stop(dwc, is_on, false);
+
+ dwc->enabled = is_on;
+
+ reg = dwc3_readl(dwc->regs, DWC3_GCTL);
+
+ if (DWC3_GCTL_PRTCAP(reg) == DWC3_GCTL_PRTCAP_DEVICE)
+ ret = dwc3_gadget_run_stop(dwc, is_on, false);
+
spin_unlock_irqrestore(&dwc->lock, flags);
return ret;
dwc->gadget_driver = driver;
+ reg = dwc3_readl(dwc->regs, DWC3_GCTL);
+ if (DWC3_GCTL_PRTCAP(reg) != DWC3_GCTL_PRTCAP_DEVICE)
+ goto mode_mismatch;
+
reg = dwc3_readl(dwc->regs, DWC3_DCFG);
reg &= ~(DWC3_DCFG_SPEED_MASK);
dwc3_gadget_enable_irq(dwc);
+mode_mismatch:
spin_unlock_irqrestore(&dwc->lock, flags);
return 0;
struct dwc3 *dwc = gadget_to_dwc(g);
unsigned long flags;
int irq;
+ u32 reg;
spin_lock_irqsave(&dwc->lock, flags);
- dwc3_gadget_disable_irq(dwc);
- __dwc3_gadget_ep_disable(dwc->eps[0]);
- __dwc3_gadget_ep_disable(dwc->eps[1]);
+ reg = dwc3_readl(dwc->regs, DWC3_GCTL);
+
+ if (DWC3_GCTL_PRTCAP(reg) == DWC3_GCTL_PRTCAP_DEVICE) {
+ dwc3_gadget_disable_irq(dwc);
+ __dwc3_gadget_ep_disable(dwc->eps[0]);
+ __dwc3_gadget_ep_disable(dwc->eps[1]);
+ }
dwc->gadget_driver = NULL;
err0:
return ret;
}
+
+static int dwc3_gadget_reinit(struct dwc3 *dwc)
+{
+ u32 hwparams4 = dwc->hwparams.hwparams4;
+ u32 reg;
+ int ret;
+ struct dwc3_ep *dep = NULL;
+
+ reg = dwc3_readl(dwc->regs, DWC3_GSNPSID);
+ /* This should read as U3 followed by revision number */
+ if ((reg & DWC3_GSNPSID_MASK) == 0x55330000) {
+ /* Detected DWC_usb3 IP */
+ dwc->revision = reg;
+ } else if ((reg & DWC3_GSNPSID_MASK) == 0x33310000) {
+ /* Detected DWC_usb31 IP */
+ dwc->revision = dwc3_readl(dwc->regs, DWC3_VER_NUMBER);
+ dwc->revision |= DWC3_REVISION_IS_DWC31;
+ } else {
+ dev_err(dwc->dev, "this is not a DesignWare USB3 DRD Core\n");
+ ret = -ENODEV;
+ goto err0;
+ }
+
+ /*
+ * Write Linux Version Code to our GUID register so it's easy to figure
+ * out which kernel version a bug was found.
+ */
+ dwc3_writel(dwc->regs, DWC3_GUID, LINUX_VERSION_CODE);
+
+ /* Handle USB2.0-only core configuration */
+ if (DWC3_GHWPARAMS3_SSPHY_IFC(dwc->hwparams.hwparams3) ==
+ DWC3_GHWPARAMS3_SSPHY_IFC_DIS) {
+ if (dwc->maximum_speed == USB_SPEED_SUPER)
+ dwc->maximum_speed = USB_SPEED_HIGH;
+ }
+
+ /* issue device SoftReset too */
+ ret = dwc3_soft_reset(dwc);
+ if (ret)
+ goto err0;
+
+ reg = dwc3_readl(dwc->regs, DWC3_GCTL);
+ reg &= ~DWC3_GCTL_SCALEDOWN_MASK;
+
+ switch (DWC3_GHWPARAMS1_EN_PWROPT(dwc->hwparams.hwparams1)) {
+ case DWC3_GHWPARAMS1_EN_PWROPT_CLK:
+ /**
+ * WORKAROUND: DWC3 revisions between 2.10a and 2.50a have an
+ * issue which would cause xHCI compliance tests to fail.
+ *
+ * Because of that we cannot enable clock gating on such
+ * configurations.
+ *
+ * Refers to:
+ *
+ * STAR#9000588375: Clock Gating, SOF Issues when ref_clk-Based
+ * SOF/ITP Mode Used
+ */
+ if ((dwc->dr_mode == USB_DR_MODE_HOST ||
+ dwc->dr_mode == USB_DR_MODE_OTG) &&
+ (dwc->revision >= DWC3_REVISION_210A &&
+ dwc->revision <= DWC3_REVISION_250A))
+ reg |= DWC3_GCTL_DSBLCLKGTNG | DWC3_GCTL_SOFITPSYNC;
+ else
+ reg &= ~DWC3_GCTL_DSBLCLKGTNG;
+ break;
+ case DWC3_GHWPARAMS1_EN_PWROPT_HIB:
+ /* enable hibernation here */
+ dwc->nr_scratch = DWC3_GHWPARAMS4_HIBER_SCRATCHBUFS(hwparams4);
+
+ /*
+ * REVISIT Enabling this bit so that host-mode hibernation
+ * will work. Device-mode hibernation is not yet implemented.
+ */
+ reg |= DWC3_GCTL_GBLHIBERNATIONEN;
+ break;
+ default:
+ dwc3_trace(trace_dwc3_core,
+ "No power optimization available\n");
+ }
+
+ /* check if current dwc3 is on simulation board */
+ if (dwc->hwparams.hwparams6 & DWC3_GHWPARAMS6_EN_FPGA) {
+ dwc3_trace(trace_dwc3_core,
+ "running on FPGA platform\n");
+ dwc->is_fpga = true;
+ }
+
+ WARN_ONCE(dwc->disable_scramble_quirk && !dwc->is_fpga,
+ "disable_scramble cannot be used on non-FPGA builds\n");
+
+ if (dwc->disable_scramble_quirk && dwc->is_fpga)
+ reg |= DWC3_GCTL_DISSCRAMBLE;
+ else
+ reg &= ~DWC3_GCTL_DISSCRAMBLE;
+
+ if (dwc->u2exit_lfps_quirk)
+ reg |= DWC3_GCTL_U2EXIT_LFPS;
+
+ /*
+ * WORKAROUND: DWC3 revisions <1.90a have a bug
+ * where the device can fail to connect at SuperSpeed
+ * and falls back to high-speed mode which causes
+ * the device to enter a Connect/Disconnect loop
+ */
+ if (dwc->revision < DWC3_REVISION_190A)
+ reg |= DWC3_GCTL_U2RSTECN;
+ dwc3_writel(dwc->regs, DWC3_GCTL, reg);
+
+ ret = dwc3_event_buffers_setup(dwc);
+
+ if (ret) {
+ dev_err(dwc->dev, "failed to setup event buffers\n");
+ goto err1;
+ }
+
+ reg = dwc3_readl(dwc->regs, DWC3_DCFG);
+ reg |= DWC3_DCFG_LPM_CAP;
+ reg &= ~(DWC3_DCFG_SPEED_MASK);
+
+ /**
+ * WORKAROUND: DWC3 revision < 2.20a have an issue
+ * which would cause metastability state on Run/Stop
+ * bit if we try to force the IP to USB2-only mode.
+ *
+ * Because of that, we cannot configure the IP to any
+ * speed other than the SuperSpeed
+ *
+ * Refers to:
+ *
+ * STAR#9000525659: Clock Domain Crossing on DCTL in
+ * USB 2.0 Mode
+ */
+ if (dwc->revision < DWC3_REVISION_220A) {
+ reg |= DWC3_DCFG_SUPERSPEED;
+ } else {
+ switch (dwc->maximum_speed) {
+ case USB_SPEED_LOW:
+ reg |= DWC3_DSTS_LOWSPEED;
+ break;
+ case USB_SPEED_FULL:
+ reg |= DWC3_DSTS_FULLSPEED1;
+ break;
+ case USB_SPEED_HIGH:
+ reg |= DWC3_DSTS_HIGHSPEED;
+ break;
+ case USB_SPEED_SUPER: /* FALLTHROUGH */
+ case USB_SPEED_UNKNOWN: /* FALTHROUGH */
+ default:
+ reg |= DWC3_DSTS_SUPERSPEED;
+ }
+ }
+ dwc3_writel(dwc->regs, DWC3_DCFG, reg);
+
+ /* 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,
+ false);
+ if (ret) {
+ dev_err(dwc->dev, "failed to enable %s\n", dep->name);
+ goto err1;
+ }
+
+ dep = dwc->eps[1];
+ ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false,
+ false);
+ if (ret) {
+ dev_err(dwc->dev, "failed to enable %s\n", dep->name);
+ goto err2;
+ }
+
+ /* begin to receive SETUP packets */
+ dwc->ep0state = EP0_SETUP_PHASE;
+ dwc3_ep0_out_start(dwc);
+
+ return 0;
+err2:
+ __dwc3_gadget_ep_disable(dwc->eps[0]);
+err1:
+ dwc3_event_buffers_cleanup(dwc);
+err0:
+ return ret;
+}
+
+int dwc3_gadget_restart(struct dwc3 *dwc, bool start)
+{
+ struct dwc3_event_buffer *evt;
+ int ret = 0;
+ int i;
+ u32 reg;
+
+ if (start) {
+ ret = dwc3_gadget_reinit(dwc);
+ if (ret < 0) {
+ dev_err(dwc->dev,
+ "dwc3 gadget reinit error = %d\n", ret);
+ goto err;
+ }
+
+ if (dwc->enabled) {
+ ret = dwc3_gadget_run_stop(dwc, start, false);
+ if (ret < 0) {
+ dev_err(dwc->dev,
+ "dwc3 gadget run stop err = %d\n", ret);
+ goto err;
+ }
+ }
+ dwc3_gadget_enable_irq(dwc);
+ } else {
+ /*
+ * Per databook, DEVCTRLHLT bit setting requires
+ * interrupts to be acknowledged. so acknowledge
+ * the events that are generated (by writing to
+ * GEVNTCOUNTn) first. And we also mask interrupts
+ * and clear SW states to avoid generating other
+ * interrupts after do gadget disconnnect operation.
+ */
+ dwc3_gadget_disable_irq(dwc);
+
+ for (i = 0; i < dwc->num_event_buffers; i++) {
+ evt = dwc->ev_buffs[i];
+ evt->count = 0;
+ evt->flags &= ~DWC3_EVENT_PENDING;
+ reg = dwc3_readl(dwc->regs, DWC3_GEVNTSIZ(i));
+ reg |= DWC3_GEVNTSIZ_INTMASK;
+ dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(i), reg);
+ reg = dwc3_readl(dwc->regs, DWC3_GEVNTCOUNT(i));
+ dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(i), reg);
+ }
+
+ /*
+ * DEVCTRLHLT bit sometimes does not get set
+ * even when GEVNTCOUNT is acked so do not
+ * care run stop function return value.
+ */
+ dwc3_gadget_run_stop(dwc, start, false);
+
+ if (dwc->gadget.state != USB_STATE_NOTATTACHED)
+ dwc3_gadget_disconnect_interrupt(dwc);
+
+ __dwc3_gadget_ep_disable(dwc->eps[0]);
+ __dwc3_gadget_ep_disable(dwc->eps[1]);
+ }
+
+err:
+ return ret;
+}