Module Name: src
Committed By: skrll
Date: Sun Feb 14 10:53:30 UTC 2016
Modified Files:
src/sys/external/bsd/dwc2/dist: dwc2_core.c dwc2_core.h dwc2_coreintr.c
dwc2_hcd.c dwc2_hcd.h dwc2_hcdddma.c dwc2_hcdintr.c dwc2_hcdqueue.c
dwc2_hw.h
Log Message:
Merge conflicts
To generate a diff of this commit:
cvs rdiff -u -r1.10 -r1.11 src/sys/external/bsd/dwc2/dist/dwc2_core.c
cvs rdiff -u -r1.6 -r1.7 src/sys/external/bsd/dwc2/dist/dwc2_core.h \
src/sys/external/bsd/dwc2/dist/dwc2_hcdddma.c
cvs rdiff -u -r1.9 -r1.10 src/sys/external/bsd/dwc2/dist/dwc2_coreintr.c
cvs rdiff -u -r1.17 -r1.18 src/sys/external/bsd/dwc2/dist/dwc2_hcd.c
cvs rdiff -u -r1.11 -r1.12 src/sys/external/bsd/dwc2/dist/dwc2_hcd.h
cvs rdiff -u -r1.12 -r1.13 src/sys/external/bsd/dwc2/dist/dwc2_hcdintr.c
cvs rdiff -u -r1.13 -r1.14 src/sys/external/bsd/dwc2/dist/dwc2_hcdqueue.c
cvs rdiff -u -r1.3 -r1.4 src/sys/external/bsd/dwc2/dist/dwc2_hw.h
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/sys/external/bsd/dwc2/dist/dwc2_core.c
diff -u src/sys/external/bsd/dwc2/dist/dwc2_core.c:1.10 src/sys/external/bsd/dwc2/dist/dwc2_core.c:1.11
--- src/sys/external/bsd/dwc2/dist/dwc2_core.c:1.10 Tue Sep 1 14:03:00 2015
+++ src/sys/external/bsd/dwc2/dist/dwc2_core.c Sun Feb 14 10:53:30 2016
@@ -1,4 +1,4 @@
-/* $NetBSD: dwc2_core.c,v 1.10 2015/09/01 14:03:00 skrll Exp $ */
+/* $NetBSD: dwc2_core.c,v 1.11 2016/02/14 10:53:30 skrll Exp $ */
/*
* core.c - DesignWare HS OTG Controller common routines
@@ -43,7 +43,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: dwc2_core.c,v 1.10 2015/09/01 14:03:00 skrll Exp $");
+__KERNEL_RCSID(0, "$NetBSD: dwc2_core.c,v 1.11 2016/02/14 10:53:30 skrll Exp $");
#include <sys/types.h>
#include <sys/bus.h>
@@ -127,6 +127,7 @@ static int dwc2_restore_host_registers(s
DWC2_WRITE_4(hsotg, HPRT0, hr->hprt0);
DWC2_WRITE_4(hsotg, HFIR, hr->hfir);
+ hsotg->frame_number = 0;
return 0;
}
@@ -408,6 +409,12 @@ int dwc2_enter_hibernation(struct dwc2_h
}
}
+ /*
+ * Clear any pending interrupts since dwc2 will not be able to
+ * clear them after entering hibernation.
+ */
+ DWC2_WRITE_4(hsotg, GINTSTS, 0xffffffff);
+
/* Put the controller in low power state */
pcgcctl = DWC2_READ_4(hsotg, PCGCTL);
@@ -485,64 +492,166 @@ static void dwc2_init_fs_ls_pclk_sel(str
* Do core a soft reset of the core. Be careful with this because it
* resets all the internal state machines of the core.
*/
-static int dwc2_core_reset(struct dwc2_hsotg *hsotg)
+int dwc2_core_reset(struct dwc2_hsotg *hsotg)
{
u32 greset;
int count = 0;
- u32 gusbcfg;
dev_vdbg(hsotg->dev, "%s()\n", __func__);
- /* Wait for AHB master IDLE state */
+ /* Core Soft Reset */
+ greset = DWC2_READ_4(hsotg, GRSTCTL);
+ greset |= GRSTCTL_CSFTRST;
+ DWC2_WRITE_4(hsotg, GRSTCTL, greset);
do {
- usleep_range(20000, 40000);
+ udelay(1);
greset = DWC2_READ_4(hsotg, GRSTCTL);
if (++count > 50) {
dev_warn(hsotg->dev,
- "%s() HANG! AHB Idle GRSTCTL=%0x\n",
+ "%s() HANG! Soft Reset GRSTCTL=%0x\n",
__func__, greset);
return -EBUSY;
}
- } while (!(greset & GRSTCTL_AHBIDLE));
+ } while (greset & GRSTCTL_CSFTRST);
- /* Core Soft Reset */
+ /* Wait for AHB master IDLE state */
count = 0;
- greset |= GRSTCTL_CSFTRST;
- DWC2_WRITE_4(hsotg, GRSTCTL, greset);
do {
- usleep_range(20000, 40000);
+ udelay(1);
greset = DWC2_READ_4(hsotg, GRSTCTL);
if (++count > 50) {
dev_warn(hsotg->dev,
- "%s() HANG! Soft Reset GRSTCTL=%0x\n",
+ "%s() HANG! AHB Idle GRSTCTL=%0x\n",
__func__, greset);
return -EBUSY;
}
- } while (greset & GRSTCTL_CSFTRST);
+ } while (!(greset & GRSTCTL_AHBIDLE));
+
+ return 0;
+}
- if (hsotg->dr_mode == USB_DR_MODE_HOST) {
- gusbcfg = DWC2_READ_4(hsotg, GUSBCFG);
- gusbcfg &= ~GUSBCFG_FORCEDEVMODE;
- gusbcfg |= GUSBCFG_FORCEHOSTMODE;
- DWC2_WRITE_4(hsotg, GUSBCFG, gusbcfg);
- } else if (hsotg->dr_mode == USB_DR_MODE_PERIPHERAL) {
- gusbcfg = DWC2_READ_4(hsotg, GUSBCFG);
- gusbcfg &= ~GUSBCFG_FORCEHOSTMODE;
- gusbcfg |= GUSBCFG_FORCEDEVMODE;
- DWC2_WRITE_4(hsotg, GUSBCFG, gusbcfg);
- } else if (hsotg->dr_mode == USB_DR_MODE_OTG) {
- gusbcfg = DWC2_READ_4(hsotg, GUSBCFG);
- gusbcfg &= ~GUSBCFG_FORCEHOSTMODE;
- gusbcfg &= ~GUSBCFG_FORCEDEVMODE;
- DWC2_WRITE_4(hsotg, GUSBCFG, gusbcfg);
+/*
+ * Force the mode of the controller.
+ *
+ * Forcing the mode is needed for two cases:
+ *
+ * 1) If the dr_mode is set to either HOST or PERIPHERAL we force the
+ * controller to stay in a particular mode regardless of ID pin
+ * changes. We do this usually after a core reset.
+ *
+ * 2) During probe we want to read reset values of the hw
+ * configuration registers that are only available in either host or
+ * device mode. We may need to force the mode if the current mode does
+ * not allow us to access the register in the mode that we want.
+ *
+ * In either case it only makes sense to force the mode if the
+ * controller hardware is OTG capable.
+ *
+ * Checks are done in this function to determine whether doing a force
+ * would be valid or not.
+ *
+ * If a force is done, it requires a 25ms delay to take effect.
+ *
+ * Returns true if the mode was forced.
+ */
+static bool dwc2_force_mode(struct dwc2_hsotg *hsotg, bool host)
+{
+ u32 gusbcfg;
+ u32 set;
+ u32 clear;
+
+ dev_dbg(hsotg->dev, "Forcing mode to %s\n", host ? "host" : "device");
+
+ /*
+ * Force mode has no effect if the hardware is not OTG.
+ */
+ if (!dwc2_hw_is_otg(hsotg))
+ 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 (host && hsotg->dr_mode == USB_DR_MODE_PERIPHERAL) {
+ WARN_ON(1);
+ return false;
}
+ if (!host && hsotg->dr_mode == USB_DR_MODE_HOST) {
+ WARN_ON(1);
+ return false;
+ }
+
+ gusbcfg = DWC2_READ_4(hsotg, GUSBCFG);
+
+ set = host ? GUSBCFG_FORCEHOSTMODE : GUSBCFG_FORCEDEVMODE;
+ clear = host ? GUSBCFG_FORCEDEVMODE : GUSBCFG_FORCEHOSTMODE;
+
+ gusbcfg &= ~clear;
+ gusbcfg |= set;
+ DWC2_WRITE_4(hsotg, GUSBCFG, gusbcfg);
+
+ msleep(25);
+ return true;
+}
+
+/*
+ * Clears the force mode bits.
+ */
+static void dwc2_clear_force_mode(struct dwc2_hsotg *hsotg)
+{
+ u32 gusbcfg;
+
+ gusbcfg = DWC2_READ_4(hsotg, GUSBCFG);
+ gusbcfg &= ~GUSBCFG_FORCEHOSTMODE;
+ gusbcfg &= ~GUSBCFG_FORCEDEVMODE;
+ DWC2_WRITE_4(hsotg, GUSBCFG, gusbcfg);
+
/*
* NOTE: This long sleep is _very_ important, otherwise the core will
* not stay in host mode after a connector ID change!
*/
- usleep_range(150000, 200000);
+ msleep(25);
+}
+
+/*
+ * Sets or clears force mode based on the dr_mode parameter.
+ */
+void dwc2_force_dr_mode(struct dwc2_hsotg *hsotg)
+{
+ switch (hsotg->dr_mode) {
+ case USB_DR_MODE_HOST:
+ dwc2_force_mode(hsotg, true);
+ break;
+ case USB_DR_MODE_PERIPHERAL:
+ dwc2_force_mode(hsotg, false);
+ break;
+ case USB_DR_MODE_OTG:
+ dwc2_clear_force_mode(hsotg);
+ break;
+ default:
+ dev_warn(hsotg->dev, "%s() Invalid dr_mode=%d\n",
+ __func__, hsotg->dr_mode);
+ break;
+ }
+}
+
+/*
+ * Do core a soft reset of the core. Be careful with this because it
+ * resets all the internal state machines of the core.
+ *
+ * Additionally this will apply force mode as per the hsotg->dr_mode
+ * parameter.
+ */
+int dwc2_core_reset_and_force_dr_mode(struct dwc2_hsotg *hsotg)
+{
+ int retval;
+ retval = dwc2_core_reset(hsotg);
+ if (retval)
+ return retval;
+
+ dwc2_force_dr_mode(hsotg);
return 0;
}
@@ -557,16 +666,20 @@ static int dwc2_fs_phy_init(struct dwc2_
*/
if (select_phy) {
dev_dbg(hsotg->dev, "FS PHY selected\n");
- usbcfg = DWC2_READ_4(hsotg, GUSBCFG);
- usbcfg |= GUSBCFG_PHYSEL;
- DWC2_WRITE_4(hsotg, GUSBCFG, usbcfg);
- /* Reset after a PHY select */
- retval = dwc2_core_reset(hsotg);
- if (retval) {
- dev_err(hsotg->dev, "%s() Reset failed, aborting",
- __func__);
- return retval;
+ usbcfg = DWC2_READ_4(hsotg, GUSBCFG);
+ if (!(usbcfg & GUSBCFG_PHYSEL)) {
+ usbcfg |= GUSBCFG_PHYSEL;
+ DWC2_WRITE_4(hsotg, GUSBCFG, usbcfg);
+
+ /* Reset after a PHY select */
+ retval = dwc2_core_reset_and_force_dr_mode(hsotg);
+
+ if (retval) {
+ dev_err(hsotg->dev,
+ "%s: Reset failed, aborting", __func__);
+ return retval;
+ }
}
}
@@ -601,13 +714,13 @@ static int dwc2_fs_phy_init(struct dwc2_
static int dwc2_hs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy)
{
- u32 usbcfg;
+ u32 usbcfg, usbcfg_old;
int retval = 0;
if (!select_phy)
return 0;
- usbcfg = DWC2_READ_4(hsotg, GUSBCFG);
+ usbcfg = usbcfg_old = DWC2_READ_4(hsotg, GUSBCFG);
/*
* HS PHY parameters. These parameters are preserved during soft reset
@@ -635,14 +748,16 @@ static int dwc2_hs_phy_init(struct dwc2_
break;
}
- DWC2_WRITE_4(hsotg, GUSBCFG, usbcfg);
+ if (usbcfg != usbcfg_old) {
+ DWC2_WRITE_4(hsotg, GUSBCFG, usbcfg);
- /* Reset after setting the PHY parameters */
- retval = dwc2_core_reset(hsotg);
- if (retval) {
- dev_err(hsotg->dev, "%s() Reset failed, aborting",
- __func__);
- return retval;
+ /* Reset after setting the PHY parameters */
+ retval = dwc2_core_reset_and_force_dr_mode(hsotg);
+ if (retval) {
+ dev_err(hsotg->dev,
+ "%s: Reset failed, aborting", __func__);
+ return retval;
+ }
}
return retval;
@@ -779,11 +894,10 @@ static void dwc2_gusbcfg_init(struct dwc
* dwc2_core_init() - Initializes the DWC_otg controller registers and
* prepares the core for device mode or host mode operation
*
- * @hsotg: Programming view of the DWC_otg controller
- * @select_phy: If true then also set the Phy type
- * @irq: If >= 0, the irq to register
+ * @hsotg: Programming view of the DWC_otg controller
+ * @initial_setup: If true then this is the first init for this instance.
*/
-int dwc2_core_init(struct dwc2_hsotg *hsotg, bool select_phy)
+int dwc2_core_init(struct dwc2_hsotg *hsotg, bool initial_setup)
{
u32 usbcfg, otgctl;
int retval;
@@ -805,18 +919,26 @@ int dwc2_core_init(struct dwc2_hsotg *hs
DWC2_WRITE_4(hsotg, GUSBCFG, usbcfg);
- /* Reset the Controller */
- retval = dwc2_core_reset(hsotg);
- if (retval) {
- dev_err(hsotg->dev, "%s(): Reset failed, aborting\n",
- __func__);
- return retval;
+ /*
+ * Reset the Controller
+ *
+ * We only need to reset the controller if this is a re-init.
+ * For the first init we know for sure that earlier code reset us (it
+ * needed to in order to properly detect various parameters).
+ */
+ if (!initial_setup) {
+ retval = dwc2_core_reset_and_force_dr_mode(hsotg);
+ if (retval) {
+ dev_err(hsotg->dev, "%s(): Reset failed, aborting\n",
+ __func__);
+ return retval;
+ }
}
/*
* This needs to happen in FS mode before any other programming occurs
*/
- retval = dwc2_phy_init(hsotg, select_phy);
+ retval = dwc2_phy_init(hsotg, initial_setup);
if (retval)
return retval;
@@ -892,7 +1014,7 @@ void dwc2_disable_host_interrupts(struct
/* Disable host mode interrupts without disturbing common interrupts */
intmsk &= ~(GINTSTS_SOF | GINTSTS_PRTINT | GINTSTS_HCHINT |
- GINTSTS_PTXFEMP | GINTSTS_NPTXFEMP);
+ GINTSTS_PTXFEMP | GINTSTS_NPTXFEMP | GINTSTS_DISCONNINT);
DWC2_WRITE_4(hsotg, GINTMSK, intmsk);
}
@@ -979,7 +1101,8 @@ static void dwc2_config_fifos(struct dwc
grxfsiz |= params->host_rx_fifo_size <<
GRXFSIZ_DEPTH_SHIFT & GRXFSIZ_DEPTH_MASK;
DWC2_WRITE_4(hsotg, GRXFSIZ, grxfsiz);
- dev_dbg(hsotg->dev, "new grxfsiz=%08x\n", DWC2_READ_4(hsotg, GRXFSIZ));
+ dev_dbg(hsotg->dev, "new grxfsiz=%08x\n",
+ DWC2_READ_4(hsotg, GRXFSIZ));
/* Non-periodic Tx FIFO */
dev_dbg(hsotg->dev, "initial gnptxfsiz=%08x\n",
@@ -1720,6 +1843,7 @@ void dwc2_hc_start_transfer(struct dwc2_
u32 hcchar;
u32 hctsiz = 0;
u16 num_packets;
+ u32 ec_mc;
if (dbg_hc(chan))
dev_vdbg(hsotg->dev, "%s()\n", __func__);
@@ -1756,6 +1880,13 @@ void dwc2_hc_start_transfer(struct dwc2_
hctsiz |= chan->xfer_len << TSIZ_XFERSIZE_SHIFT &
TSIZ_XFERSIZE_MASK;
+
+ /* For split set ec_mc for immediate retries */
+ if (chan->ep_type == USB_ENDPOINT_XFER_INT ||
+ chan->ep_type == USB_ENDPOINT_XFER_ISOC)
+ ec_mc = 3;
+ else
+ ec_mc = 1;
} else {
if (dbg_hc(chan))
dev_vdbg(hsotg->dev, "no split\n");
@@ -1818,6 +1949,9 @@ void dwc2_hc_start_transfer(struct dwc2_
hctsiz |= chan->xfer_len << TSIZ_XFERSIZE_SHIFT &
TSIZ_XFERSIZE_MASK;
+
+ /* The ec_mc gets the multi_count for non-split */
+ ec_mc = chan->multi_count;
}
chan->start_pkt_count = num_packets;
@@ -1876,8 +2010,7 @@ void dwc2_hc_start_transfer(struct dwc2_
hcchar = DWC2_READ_4(hsotg, HCCHAR(chan->hc_num));
hcchar &= ~HCCHAR_MULTICNT_MASK;
- hcchar |= chan->multi_count << HCCHAR_MULTICNT_SHIFT &
- HCCHAR_MULTICNT_MASK;
+ hcchar |= (ec_mc << HCCHAR_MULTICNT_SHIFT) & HCCHAR_MULTICNT_MASK;
dwc2_hc_set_even_odd_frame(hsotg, chan, &hcchar);
if (hcchar & HCCHAR_CHDIS)
@@ -1926,7 +2059,6 @@ void dwc2_hc_start_transfer_ddma(struct
struct dwc2_host_chan *chan)
{
u32 hcchar;
- u32 hc_dma;
u32 hctsiz = 0;
if (chan->do_ping)
@@ -1955,15 +2087,22 @@ void dwc2_hc_start_transfer_ddma(struct
DWC2_WRITE_4(hsotg, HCTSIZ(chan->hc_num), hctsiz);
- hc_dma = (u32)chan->desc_list_addr & HCDMA_DMA_ADDR_MASK;
-
- /* Always start from first descriptor */
- hc_dma &= ~HCDMA_CTD_MASK;
- DWC2_WRITE_4(hsotg, HCDMA(chan->hc_num), hc_dma);
- if (dbg_hc(chan))
- dev_vdbg(hsotg->dev, "Wrote %08x to HCDMA(%d)\n",
- hc_dma, chan->hc_num);
+ usb_syncmem(&chan->desc_list_usbdma, 0, chan->desc_list_sz,
+ BUS_DMASYNC_PREWRITE);
+ if (hsotg->hsotg_sc->sc_set_dma_addr == NULL) {
+ DWC2_WRITE_4(hsotg, HCDMA(chan->hc_num), chan->desc_list_addr);
+ if (dbg_hc(chan))
+ dev_vdbg(hsotg->dev, "Wrote %pad to HCDMA(%d)\n",
+ &chan->desc_list_addr, chan->hc_num);
+ } else {
+ (void)(*hsotg->hsotg_sc->sc_set_dma_addr)(
+ hsotg->dev, chan->desc_list_addr, chan->hc_num);
+ if (dbg_hc(chan))
+ dev_vdbg(hsotg->dev, "Wrote %pad to ext dma(%d)\n",
+ &chan->desc_list_addr, chan->hc_num);
+ }
+
hcchar = DWC2_READ_4(hsotg, HCCHAR(chan->hc_num));
hcchar &= ~HCCHAR_MULTICNT_MASK;
hcchar |= chan->multi_count << HCCHAR_MULTICNT_SHIFT &
@@ -2505,6 +2644,29 @@ void dwc2_set_param_dma_desc_enable(stru
hsotg->core_params->dma_desc_enable = val;
}
+void dwc2_set_param_dma_desc_fs_enable(struct dwc2_hsotg *hsotg, int val)
+{
+ int valid = 1;
+
+ if (val > 0 && (hsotg->core_params->dma_enable <= 0 ||
+ !hsotg->hw_params.dma_desc_enable))
+ valid = 0;
+ if (val < 0)
+ valid = 0;
+
+ if (!valid) {
+ if (val >= 0)
+ dev_err(hsotg->dev,
+ "%d invalid for dma_desc_fs_enable parameter. Check HW configuration.\n",
+ val);
+ val = (hsotg->core_params->dma_enable > 0 &&
+ hsotg->hw_params.dma_desc_enable);
+ }
+
+ hsotg->core_params->dma_desc_fs_enable = val;
+ dev_dbg(hsotg->dev, "Setting dma_desc_fs_enable to %d\n", val);
+}
+
void dwc2_set_param_host_support_fs_ls_low_power(struct dwc2_hsotg *hsotg,
int val)
{
@@ -3036,6 +3198,7 @@ void dwc2_set_parameters(struct dwc2_hso
dwc2_set_param_otg_cap(hsotg, params->otg_cap);
dwc2_set_param_dma_enable(hsotg, params->dma_enable);
dwc2_set_param_dma_desc_enable(hsotg, params->dma_desc_enable);
+ dwc2_set_param_dma_desc_fs_enable(hsotg, params->dma_desc_fs_enable);
dwc2_set_param_host_support_fs_ls_low_power(hsotg,
params->host_support_fs_ls_low_power);
dwc2_set_param_enable_dynamic_fifo(hsotg,
@@ -3072,6 +3235,79 @@ void dwc2_set_parameters(struct dwc2_hso
dwc2_set_param_hibernation(hsotg, params->hibernation);
}
+/*
+ * Forces either host or device mode if the controller is not
+ * currently in that mode.
+ *
+ * Returns true if the mode was forced.
+ */
+static bool dwc2_force_mode_if_needed(struct dwc2_hsotg *hsotg, bool host)
+{
+ if (host && dwc2_is_host_mode(hsotg))
+ return false;
+ else if (!host && dwc2_is_device_mode(hsotg))
+ return false;
+
+ return dwc2_force_mode(hsotg, host);
+}
+
+/*
+ * Gets host hardware parameters. Forces host mode if not currently in
+ * host mode. Should be called immediately after a core soft reset in
+ * order to get the reset values.
+ */
+static void dwc2_get_host_hwparams(struct dwc2_hsotg *hsotg)
+{
+ struct dwc2_hw_params *hw = &hsotg->hw_params;
+ u32 gnptxfsiz;
+ u32 hptxfsiz;
+ bool forced;
+
+ if (hsotg->dr_mode == USB_DR_MODE_PERIPHERAL)
+ return;
+
+ forced = dwc2_force_mode_if_needed(hsotg, true);
+
+ gnptxfsiz = DWC2_READ_4(hsotg, GNPTXFSIZ);
+ hptxfsiz = DWC2_READ_4(hsotg, HPTXFSIZ);
+ dev_dbg(hsotg->dev, "gnptxfsiz=%08x\n", gnptxfsiz);
+ dev_dbg(hsotg->dev, "hptxfsiz=%08x\n", hptxfsiz);
+
+ if (forced)
+ dwc2_clear_force_mode(hsotg);
+
+ hw->host_nperio_tx_fifo_size = (gnptxfsiz & FIFOSIZE_DEPTH_MASK) >>
+ FIFOSIZE_DEPTH_SHIFT;
+ hw->host_perio_tx_fifo_size = (hptxfsiz & FIFOSIZE_DEPTH_MASK) >>
+ FIFOSIZE_DEPTH_SHIFT;
+}
+
+/*
+ * Gets device hardware parameters. Forces device mode if not
+ * currently in device mode. Should be called immediately after a core
+ * soft reset in order to get the reset values.
+ */
+static void dwc2_get_dev_hwparams(struct dwc2_hsotg *hsotg)
+{
+ struct dwc2_hw_params *hw = &hsotg->hw_params;
+ bool forced;
+ u32 gnptxfsiz;
+
+ if (hsotg->dr_mode == USB_DR_MODE_HOST)
+ return;
+
+ forced = dwc2_force_mode_if_needed(hsotg, false);
+
+ gnptxfsiz = DWC2_READ_4(hsotg, GNPTXFSIZ);
+ dev_dbg(hsotg->dev, "gnptxfsiz=%08x\n", gnptxfsiz);
+
+ if (forced)
+ dwc2_clear_force_mode(hsotg);
+
+ hw->dev_nperio_tx_fifo_size = (gnptxfsiz & FIFOSIZE_DEPTH_MASK) >>
+ FIFOSIZE_DEPTH_SHIFT;
+}
+
/**
* During device initialization, read various hardware configuration
* registers and interpret the contents.
@@ -3080,9 +3316,8 @@ int dwc2_get_hwparams(struct dwc2_hsotg
{
struct dwc2_hw_params *hw = &hsotg->hw_params;
unsigned width;
- u32 hwcfg2, hwcfg3, hwcfg4;
- u32 hptxfsiz, grxfsiz, gnptxfsiz;
- u32 gusbcfg;
+ u32 hwcfg1, hwcfg2, hwcfg3, hwcfg4;
+ u32 grxfsiz;
/*
* Attempt to ensure this device is really a DWC_otg Controller.
@@ -3102,31 +3337,28 @@ int dwc2_get_hwparams(struct dwc2_hsotg
hw->snpsid >> 12 & 0xf, hw->snpsid >> 8 & 0xf,
hw->snpsid >> 4 & 0xf, hw->snpsid & 0xf, hw->snpsid);
+ hwcfg1 = DWC2_READ_4(hsotg, GHWCFG1);
hwcfg2 = DWC2_READ_4(hsotg, GHWCFG2);
hwcfg3 = DWC2_READ_4(hsotg, GHWCFG3);
hwcfg4 = DWC2_READ_4(hsotg, GHWCFG4);
grxfsiz = DWC2_READ_4(hsotg, GRXFSIZ);
- dev_dbg(hsotg->dev, "hwcfg1=%08x\n", DWC2_READ_4(hsotg, GHWCFG1));
+ dev_dbg(hsotg->dev, "hwcfg1=%08x\n", hwcfg1);
dev_dbg(hsotg->dev, "hwcfg2=%08x\n", hwcfg2);
dev_dbg(hsotg->dev, "hwcfg3=%08x\n", hwcfg3);
dev_dbg(hsotg->dev, "hwcfg4=%08x\n", hwcfg4);
dev_dbg(hsotg->dev, "grxfsiz=%08x\n", grxfsiz);
- /* Force host mode to get HPTXFSIZ / GNPTXFSIZ exact power on value */
- gusbcfg = DWC2_READ_4(hsotg, GUSBCFG);
- gusbcfg |= GUSBCFG_FORCEHOSTMODE;
- DWC2_WRITE_4(hsotg, GUSBCFG, gusbcfg);
- usleep_range(100000, 150000);
+ /*
+ * Host specific hardware parameters. Reading these parameters
+ * requires the controller to be in host mode. The mode will
+ * be forced, if necessary, to read these values.
+ */
+ dwc2_get_host_hwparams(hsotg);
+ dwc2_get_dev_hwparams(hsotg);
- gnptxfsiz = DWC2_READ_4(hsotg, GNPTXFSIZ);
- hptxfsiz = DWC2_READ_4(hsotg, HPTXFSIZ);
- dev_dbg(hsotg->dev, "gnptxfsiz=%08x\n", gnptxfsiz);
- dev_dbg(hsotg->dev, "hptxfsiz=%08x\n", hptxfsiz);
- gusbcfg = DWC2_READ_4(hsotg, GUSBCFG);
- gusbcfg &= ~GUSBCFG_FORCEHOSTMODE;
- DWC2_WRITE_4(hsotg, GUSBCFG, gusbcfg);
- usleep_range(100000, 150000);
+ /* hwcfg1 */
+ hw->dev_ep_dirs = hwcfg1;
/* hwcfg2 */
hw->op_mode = (hwcfg2 & GHWCFG2_OP_MODE_MASK) >>
@@ -3182,10 +3414,6 @@ int dwc2_get_hwparams(struct dwc2_hsotg
/* fifo sizes */
hw->host_rx_fifo_size = (grxfsiz & GRXFSIZ_DEPTH_MASK) >>
GRXFSIZ_DEPTH_SHIFT;
- hw->host_nperio_tx_fifo_size = (gnptxfsiz & FIFOSIZE_DEPTH_MASK) >>
- FIFOSIZE_DEPTH_SHIFT;
- hw->host_perio_tx_fifo_size = (hptxfsiz & FIFOSIZE_DEPTH_MASK) >>
- FIFOSIZE_DEPTH_SHIFT;
dev_dbg(hsotg->dev, "Detected values from hardware:\n");
dev_dbg(hsotg->dev, " op_mode=%d\n",
@@ -3202,7 +3430,7 @@ int dwc2_get_hwparams(struct dwc2_hsotg
hw->hs_phy_type);
dev_dbg(hsotg->dev, " fs_phy_type=%d\n",
hw->fs_phy_type);
- dev_dbg(hsotg->dev, " utmi_phy_data_wdith=%d\n",
+ dev_dbg(hsotg->dev, " utmi_phy_data_width=%d\n",
hw->utmi_phy_data_width);
dev_dbg(hsotg->dev, " num_dev_ep=%d\n",
hw->num_dev_ep);
@@ -3293,3 +3521,40 @@ void dwc2_disable_global_interrupts(stru
ahbcfg &= ~GAHBCFG_GLBL_INTR_EN;
DWC2_WRITE_4(hsotg, GAHBCFG, ahbcfg);
}
+
+/* Returns the controller's GHWCFG2.OTG_MODE. */
+unsigned dwc2_op_mode(struct dwc2_hsotg *hsotg)
+{
+ u32 ghwcfg2 = DWC2_READ_4(hsotg, GHWCFG2);
+
+ return (ghwcfg2 & GHWCFG2_OP_MODE_MASK) >>
+ GHWCFG2_OP_MODE_SHIFT;
+}
+
+/* Returns true if the controller is capable of DRD. */
+bool dwc2_hw_is_otg(struct dwc2_hsotg *hsotg)
+{
+ unsigned op_mode = dwc2_op_mode(hsotg);
+
+ return (op_mode == GHWCFG2_OP_MODE_HNP_SRP_CAPABLE) ||
+ (op_mode == GHWCFG2_OP_MODE_SRP_ONLY_CAPABLE) ||
+ (op_mode == GHWCFG2_OP_MODE_NO_HNP_SRP_CAPABLE);
+}
+
+/* Returns true if the controller is host-only. */
+bool dwc2_hw_is_host(struct dwc2_hsotg *hsotg)
+{
+ unsigned op_mode = dwc2_op_mode(hsotg);
+
+ return (op_mode == GHWCFG2_OP_MODE_SRP_CAPABLE_HOST) ||
+ (op_mode == GHWCFG2_OP_MODE_NO_SRP_CAPABLE_HOST);
+}
+
+/* Returns true if the controller is device-only. */
+bool dwc2_hw_is_device(struct dwc2_hsotg *hsotg)
+{
+ unsigned op_mode = dwc2_op_mode(hsotg);
+
+ return (op_mode == GHWCFG2_OP_MODE_SRP_CAPABLE_DEVICE) ||
+ (op_mode == GHWCFG2_OP_MODE_NO_SRP_CAPABLE_DEVICE);
+}
Index: src/sys/external/bsd/dwc2/dist/dwc2_core.h
diff -u src/sys/external/bsd/dwc2/dist/dwc2_core.h:1.6 src/sys/external/bsd/dwc2/dist/dwc2_core.h:1.7
--- src/sys/external/bsd/dwc2/dist/dwc2_core.h:1.6 Sun Aug 30 12:59:59 2015
+++ src/sys/external/bsd/dwc2/dist/dwc2_core.h Sun Feb 14 10:53:30 2016
@@ -1,4 +1,4 @@
-/* $NetBSD: dwc2_core.h,v 1.6 2015/08/30 12:59:59 skrll Exp $ */
+/* $NetBSD: dwc2_core.h,v 1.7 2016/02/14 10:53:30 skrll Exp $ */
/*
* core.h - DesignWare HS OTG Controller common declarations
@@ -55,8 +55,8 @@
#if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
-/* s3c-hsotg declarations */
-static const char * const s3c_hsotg_supply_names[] = {
+/* dwc2-hsotg declarations */
+static const char * const dwc2_hsotg_supply_names[] = {
"vusb_d", /* digital USB supply, 1.2V */
"vusb_a", /* analog USB supply, 1.1V */
};
@@ -82,10 +82,10 @@ static const char * const s3c_hsotg_supp
#define EP0_MPS_LIMIT 64
struct dwc2_hsotg;
-struct s3c_hsotg_req;
+struct dwc2_hsotg_req;
/**
- * struct s3c_hsotg_ep - driver endpoint definition.
+ * struct dwc2_hsotg_ep - driver endpoint definition.
* @ep: The gadget layer representation of the endpoint.
* @name: The driver generated name for the endpoint.
* @queue: Queue of requests for this endpoint.
@@ -124,11 +124,11 @@ struct s3c_hsotg_req;
* as in shared-fifo mode periodic in acts like a single-frame packet
* buffer than a fifo)
*/
-struct s3c_hsotg_ep {
+struct dwc2_hsotg_ep {
struct usb_ep ep;
struct list_head queue;
struct dwc2_hsotg *parent;
- struct s3c_hsotg_req *req;
+ struct dwc2_hsotg_req *req;
struct dentry *debugfs;
unsigned long total_data;
@@ -147,17 +147,18 @@ struct s3c_hsotg_ep {
unsigned int periodic:1;
unsigned int isochronous:1;
unsigned int send_zlp:1;
+ unsigned int has_correct_parity:1;
char name[10];
};
/**
- * struct s3c_hsotg_req - data transfer request
+ * struct dwc2_hsotg_req - data transfer request
* @req: The USB gadget request
* @queue: The list of requests for the endpoint this is queued for.
* @saved_req_buf: variable to save req.buf when bounce buffers are used.
*/
-struct s3c_hsotg_req {
+struct dwc2_hsotg_req {
struct usb_request req;
struct list_head queue;
void *saved_req_buf;
@@ -225,6 +226,13 @@ enum dwc2_ep0_state {
* value for this if none is specified.
* 0 - Address DMA
* 1 - Descriptor DMA (default, if available)
+ * @dma_desc_fs_enable: When DMA mode is enabled, specifies whether to use
+ * address DMA mode or descriptor DMA mode for accessing
+ * the data FIFOs in Full Speed mode only. The driver
+ * will automatically detect the value for this if none is
+ * specified.
+ * 0 - Address DMA
+ * 1 - Descriptor DMA in FS (default, if available)
* @speed: Specifies the maximum speed of operation in host and
* device mode. The actual speed depends on the speed of
* the attached device and the value of phy_type.
@@ -354,6 +362,7 @@ struct dwc2_core_params {
int otg_ver;
int dma_enable;
int dma_desc_enable;
+ int dma_desc_fs_enable;
int speed;
int enable_dynamic_fifo;
int en_multiple_tx_fifo;
@@ -430,15 +439,18 @@ struct dwc2_core_params {
* 1 - 16 bits
* 2 - 8 or 16 bits
* @snpsid: Value from SNPSID register
+ * @dev_ep_dirs: Direction of device endpoints (GHWCFG1)
*/
struct dwc2_hw_params {
unsigned op_mode:3;
unsigned arch:2;
unsigned dma_desc_enable:1;
+ unsigned dma_desc_fs_enable:1;
unsigned enable_dynamic_fifo:1;
unsigned en_multiple_tx_fifo:1;
unsigned host_rx_fifo_size:16;
unsigned host_nperio_tx_fifo_size:16;
+ unsigned dev_nperio_tx_fifo_size:16;
unsigned host_perio_tx_fifo_size:16;
unsigned nperio_tx_q_depth:3;
unsigned host_perio_tx_q_depth:3;
@@ -455,6 +467,7 @@ struct dwc2_hw_params {
unsigned power_optimized:1;
unsigned utmi_phy_data_width:2;
u32 snpsid;
+ u32 dev_ep_dirs;
};
/* Size of control and EP0 buffers */
@@ -558,6 +571,15 @@ struct dwc2_hregs_backup {
* - USB_DR_MODE_PERIPHERAL
* - USB_DR_MODE_HOST
* - USB_DR_MODE_OTG
+ * @hcd_enabled Host mode sub-driver initialization indicator.
+ * @gadget_enabled Peripheral mode sub-driver initialization indicator.
+ * @ll_hw_enabled Status of low-level hardware resources.
+ * @phy: The otg phy transceiver structure for phy control.
+ * @uphy: The otg phy transceiver structure for old USB phy control.
+ * @plat: The platform specific configuration data. This can be removed once
+ * all SoCs support usb transceiver.
+ * @supplies: Definition of USB power supplies
+ * @phyif: PHY interface width
* @lock: Spinlock that protects all the driver data structures
* @priv: Stores a pointer to the struct usb_hcd
* @queuing_high_bandwidth: True if multiple packets of a high-bandwidth
@@ -646,16 +668,13 @@ struct dwc2_hregs_backup {
* @otg_port: OTG port number
* @frame_list: Frame list
* @frame_list_dma: Frame list DMA address
+ * @frame_list_sz: Frame list size
+ * @desc_gen_cache: Kmem cache for generic descriptors
+ * @desc_hsisoc_cache: Kmem cache for hs isochronous descriptors
*
* These are for peripheral mode:
*
* @driver: USB gadget driver
- * @phy: The otg phy transceiver structure for phy control.
- * @uphy: The otg phy transceiver structure for old USB phy control.
- * @plat: The platform specific configuration data. This can be removed once
- * all SoCs support usb transceiver.
- * @supplies: Definition of USB power supplies
- * @phyif: PHY interface width
* @dedicated_fifos: Set if the hardware has dedicated IN-EP fifos.
* @num_of_eps: Number of available EPs (excluding EP0)
* @debug_root: Root directrory for debugfs.
@@ -668,7 +687,6 @@ struct dwc2_hregs_backup {
* @ctrl_req: Request for EP0 control packets.
* @ep0_state: EP0 control transfers state
* @test_mode: USB test mode requested by the host
- * @last_rst: Time of last reset
* @eps: The endpoints being supplied to the gadget framework
* @g_using_dma: Indicate if dma usage is enabled
* @g_rx_fifo_sz: Contains rx fifo size value
@@ -686,15 +704,18 @@ struct dwc2_hsotg {
enum usb_dr_mode dr_mode;
unsigned int hcd_enabled:1;
unsigned int gadget_enabled:1;
+ unsigned int ll_hw_enabled:1;
spinlock_t lock;
void *priv;
struct usb_phy *uphy;
#if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
struct phy *phy;
- struct regulator_bulk_data supplies[ARRAY_SIZE(s3c_hsotg_supply_names)];
+ struct usb_phy *uphy;
+ struct dwc2_hsotg_plat *plat;
+ struct regulator_bulk_data supplies[ARRAY_SIZE(dwc2_hsotg_supply_names)];
+ u32 phyif;
- struct mutex init_mutex;
int irq;
struct clk *clk;
#endif /* CONFIG_USB_DWC2_PERIPHERAL || CONFIG_USB_DWC2_DUAL_ROLE */
@@ -746,6 +767,8 @@ struct dwc2_hsotg {
u16 frame_usecs[8];
u16 frame_number;
u16 periodic_qh_count;
+ bool bus_suspended;
+ bool new_connection;
#ifdef CONFIG_USB_DWC2_TRACK_MISSED_SOFS
#define FRAME_NUM_ARRAY_SIZE 1000
@@ -772,6 +795,9 @@ struct dwc2_hsotg {
usb_dma_t frame_list_usbdma;
u32 *frame_list;
dma_addr_t frame_list_dma;
+ u32 frame_list_sz;
+ struct kmem_cache *desc_gen_cache;
+ struct kmem_cache *desc_hsisoc_cache;
#ifdef DEBUG
u32 frrem_samples;
@@ -796,9 +822,6 @@ struct dwc2_hsotg {
#if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
/* Gadget structures */
struct usb_gadget_driver *driver;
- struct s3c_hsotg_plat *plat;
-
- u32 phyif;
int fifo_mem;
unsigned int dedicated_fifos:1;
unsigned char num_of_eps;
@@ -814,9 +837,8 @@ struct dwc2_hsotg {
struct usb_gadget gadget;
unsigned int enabled:1;
unsigned int connected:1;
- unsigned long last_rst;
- struct s3c_hsotg_ep *eps_in[MAX_EPS_CHANNELS];
- struct s3c_hsotg_ep *eps_out[MAX_EPS_CHANNELS];
+ struct dwc2_hsotg_ep *eps_in[MAX_EPS_CHANNELS];
+ struct dwc2_hsotg_ep *eps_out[MAX_EPS_CHANNELS];
u32 g_using_dma;
u32 g_rx_fifo_sz;
u32 g_np_g_tx_fifo_sz;
@@ -846,10 +868,14 @@ enum dwc2_halt_status {
* The following functions support initialization of the core driver component
* and the DWC_otg controller
*/
+extern int dwc2_core_reset(struct dwc2_hsotg *hsotg);
+extern int dwc2_core_reset_and_force_dr_mode(struct dwc2_hsotg *hsotg);
extern void dwc2_core_host_init(struct dwc2_hsotg *hsotg);
extern int dwc2_enter_hibernation(struct dwc2_hsotg *hsotg);
extern int dwc2_exit_hibernation(struct dwc2_hsotg *hsotg, bool restore);
+void dwc2_force_dr_mode(struct dwc2_hsotg *hsotg);
+
/*
* Host core Functions.
* The following functions support managing the DWC_otg controller in host
@@ -883,7 +909,7 @@ extern void dwc2_read_packet(struct dwc2
extern void dwc2_flush_tx_fifo(struct dwc2_hsotg *hsotg, const int num);
extern void dwc2_flush_rx_fifo(struct dwc2_hsotg *hsotg);
-extern int dwc2_core_init(struct dwc2_hsotg *hsotg, bool select_phy);
+extern int dwc2_core_init(struct dwc2_hsotg *hsotg, bool initial_setup);
extern void dwc2_enable_global_interrupts(struct dwc2_hsotg *hcd);
extern void dwc2_disable_global_interrupts(struct dwc2_hsotg *hcd);
@@ -924,6 +950,16 @@ extern void dwc2_set_param_dma_enable(st
extern void dwc2_set_param_dma_desc_enable(struct dwc2_hsotg *hsotg, int val);
/*
+ * When DMA mode is enabled specifies whether to use
+ * address DMA or DMA Descritor mode with full speed devices
+ * for accessing the data FIFOs in host mode.
+ * 0 - address DMA
+ * 1 - FS DMA Descriptor(default, if available)
+ */
+extern void dwc2_set_param_dma_desc_fs_enable(struct dwc2_hsotg *hsotg,
+ int val);
+
+/*
* Specifies the maximum speed of operation in host and device mode.
* The actual speed depends on the speed of the attached device and
* the value of phy_type. The actual speed depends on the speed of the
@@ -1088,7 +1124,34 @@ extern void dwc2_set_all_params(struct d
extern int dwc2_get_hwparams(struct dwc2_hsotg *hsotg);
+extern int dwc2_lowlevel_hw_enable(struct dwc2_hsotg *hsotg);
+extern int dwc2_lowlevel_hw_disable(struct dwc2_hsotg *hsotg);
+
+/*
+ * The following functions check the controller's OTG operation mode
+ * capability (GHWCFG2.OTG_MODE).
+ *
+ * These functions can be used before the internal hsotg->hw_params
+ * are read in and cached so they always read directly from the
+ * GHWCFG2 register.
+ */
+unsigned dwc2_op_mode(struct dwc2_hsotg *hsotg);
+bool dwc2_hw_is_otg(struct dwc2_hsotg *hsotg);
+bool dwc2_hw_is_host(struct dwc2_hsotg *hsotg);
+bool dwc2_hw_is_device(struct dwc2_hsotg *hsotg);
+
+/*
+ * Returns the mode of operation, host or device
+ */
+static inline int dwc2_is_host_mode(struct dwc2_hsotg *hsotg)
+{
+ return (DWC2_READ_4(hsotg, GINTSTS) & GINTSTS_CURMODE_HOST) != 0;
+}
+static inline int dwc2_is_device_mode(struct dwc2_hsotg *hsotg)
+{
+ return (DWC2_READ_4(hsotg, GINTSTS) & GINTSTS_CURMODE_HOST) == 0;
+}
/*
* Dump core registers and SPRAM
@@ -1104,30 +1167,30 @@ extern u16 dwc2_get_otg_version(struct d
/* Gadget defines */
#if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
-extern int s3c_hsotg_remove(struct dwc2_hsotg *hsotg);
-extern int s3c_hsotg_suspend(struct dwc2_hsotg *dwc2);
-extern int s3c_hsotg_resume(struct dwc2_hsotg *dwc2);
+extern int dwc2_hsotg_remove(struct dwc2_hsotg *hsotg);
+extern int dwc2_hsotg_suspend(struct dwc2_hsotg *dwc2);
+extern int dwc2_hsotg_resume(struct dwc2_hsotg *dwc2);
extern int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq);
-extern void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *dwc2,
+extern void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *dwc2,
bool reset);
-extern void s3c_hsotg_core_connect(struct dwc2_hsotg *hsotg);
-extern void s3c_hsotg_disconnect(struct dwc2_hsotg *dwc2);
-extern int s3c_hsotg_set_test_mode(struct dwc2_hsotg *hsotg, int testmode);
+extern void dwc2_hsotg_core_connect(struct dwc2_hsotg *hsotg);
+extern void dwc2_hsotg_disconnect(struct dwc2_hsotg *dwc2);
+extern int dwc2_hsotg_set_test_mode(struct dwc2_hsotg *hsotg, int testmode);
#define dwc2_is_device_connected(hsotg) (hsotg->connected)
#else
-static inline int s3c_hsotg_remove(struct dwc2_hsotg *dwc2)
+static inline int dwc2_hsotg_remove(struct dwc2_hsotg *dwc2)
{ return 0; }
-static inline int s3c_hsotg_suspend(struct dwc2_hsotg *dwc2)
+static inline int dwc2_hsotg_suspend(struct dwc2_hsotg *dwc2)
{ return 0; }
-static inline int s3c_hsotg_resume(struct dwc2_hsotg *dwc2)
+static inline int dwc2_hsotg_resume(struct dwc2_hsotg *dwc2)
{ return 0; }
static inline int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq)
{ return 0; }
-static inline void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *dwc2,
+static inline void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *dwc2,
bool reset) {}
-static inline void s3c_hsotg_core_connect(struct dwc2_hsotg *hsotg) {}
-static inline void s3c_hsotg_disconnect(struct dwc2_hsotg *dwc2) {}
-static inline int s3c_hsotg_set_test_mode(struct dwc2_hsotg *hsotg,
+static inline void dwc2_hsotg_core_connect(struct dwc2_hsotg *hsotg) {}
+static inline void dwc2_hsotg_disconnect(struct dwc2_hsotg *dwc2) {}
+static inline int dwc2_hsotg_set_test_mode(struct dwc2_hsotg *hsotg,
int testmode)
{ return 0; }
#define dwc2_is_device_connected(hsotg) (0)
@@ -1135,12 +1198,14 @@ static inline int s3c_hsotg_set_test_mod
#if IS_ENABLED(CONFIG_USB_DWC2_HOST) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
extern int dwc2_hcd_get_frame_number(struct dwc2_hsotg *hsotg);
-extern void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg);
+extern void dwc2_hcd_connect(struct dwc2_hsotg *hsotg);
+extern void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg, bool force);
extern void dwc2_hcd_start(struct dwc2_hsotg *hsotg);
#else
static inline int dwc2_hcd_get_frame_number(struct dwc2_hsotg *hsotg)
{ return 0; }
-static inline void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg) {}
+static inline void dwc2_hcd_connect(struct dwc2_hsotg *hsotg) {}
+static inline void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg, bool force) {}
static inline void dwc2_hcd_start(struct dwc2_hsotg *hsotg) {}
static inline void dwc2_hcd_remove(struct dwc2_hsotg *hsotg) {}
static inline int dwc2_hcd_init(struct dwc2_hsotg *hsotg)
Index: src/sys/external/bsd/dwc2/dist/dwc2_hcdddma.c
diff -u src/sys/external/bsd/dwc2/dist/dwc2_hcdddma.c:1.6 src/sys/external/bsd/dwc2/dist/dwc2_hcdddma.c:1.7
--- src/sys/external/bsd/dwc2/dist/dwc2_hcdddma.c:1.6 Thu Apr 3 06:34:58 2014
+++ src/sys/external/bsd/dwc2/dist/dwc2_hcdddma.c Sun Feb 14 10:53:30 2016
@@ -1,4 +1,4 @@
-/* $NetBSD: dwc2_hcdddma.c,v 1.6 2014/04/03 06:34:58 skrll Exp $ */
+/* $NetBSD: dwc2_hcdddma.c,v 1.7 2016/02/14 10:53:30 skrll Exp $ */
/*
* hcd_ddma.c - DesignWare HS OTG Controller descriptor DMA routines
@@ -40,7 +40,7 @@
* This file contains the Descriptor DMA implementation for Host mode
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: dwc2_hcdddma.c,v 1.6 2014/04/03 06:34:58 skrll Exp $");
+__KERNEL_RCSID(0, "$NetBSD: dwc2_hcdddma.c,v 1.7 2016/02/14 10:53:30 skrll Exp $");
#include <sys/param.h>
#include <sys/types.h>
@@ -102,20 +102,17 @@ static int dwc2_desc_list_alloc(struct d
KASSERT(!cpu_intr_p() && !cpu_softintr_p());
qh->desc_list = NULL;
- err = usb_allocmem(&hsotg->hsotg_sc->sc_bus,
- sizeof(struct dwc2_hcd_dma_desc) * dwc2_max_desc_num(qh), 0,
- &qh->desc_list_usbdma);
+ qh->desc_list_sz = sizeof(struct dwc2_hcd_dma_desc) *
+ dwc2_max_desc_num(qh);
- if (!err) {
- qh->desc_list = KERNADDR(&qh->desc_list_usbdma, 0);
- qh->desc_list_dma = DMAADDR(&qh->desc_list_usbdma, 0);
- }
+ err = usb_allocmem(&hsotg->hsotg_sc->sc_bus, qh->desc_list_sz, 0,
+ &qh->desc_list_usbdma);
- if (!qh->desc_list)
+ if (err)
return -ENOMEM;
- memset(qh->desc_list, 0,
- sizeof(struct dwc2_hcd_dma_desc) * dwc2_max_desc_num(qh));
+ qh->desc_list = KERNADDR(&qh->desc_list_usbdma, 0);
+ qh->desc_list_dma = DMAADDR(&qh->desc_list_usbdma, 0);
qh->n_bytes = kmem_zalloc(sizeof(u32) * dwc2_max_desc_num(qh), KM_SLEEP);
if (!qh->n_bytes) {
@@ -129,6 +126,7 @@ static int dwc2_desc_list_alloc(struct d
static void dwc2_desc_list_free(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
{
+
if (qh->desc_list) {
usb_freemem(&hsotg->hsotg_sc->sc_bus, &qh->desc_list_usbdma);
qh->desc_list = NULL;
@@ -146,8 +144,9 @@ static int dwc2_frame_list_alloc(struct
return 0;
/* XXXNH - pool_cache_t */
+ hsotg->frame_list_sz = 4 * FRLISTEN_64_SIZE;
hsotg->frame_list = NULL;
- err = usb_allocmem(&hsotg->hsotg_sc->sc_bus, 4 * FRLISTEN_64_SIZE,
+ err = usb_allocmem(&hsotg->hsotg_sc->sc_bus, hsotg->frame_list_sz,
0, &hsotg->frame_list_usbdma);
if (!err) {
@@ -158,7 +157,6 @@ static int dwc2_frame_list_alloc(struct
if (!hsotg->frame_list)
return -ENOMEM;
- memset(hsotg->frame_list, 0, 4 * FRLISTEN_64_SIZE);
return 0;
}
@@ -269,6 +267,13 @@ static void dwc2_update_frame_list(struc
j = (j + inc) & (FRLISTEN_64_SIZE - 1);
} while (j != i);
+ /*
+ * Sync frame list since controller will access it if periodic
+ * channel is currently enabled.
+ */
+ usb_syncmem(&hsotg->frame_list_usbdma, 0, hsotg->frame_list_sz,
+ BUS_DMASYNC_PREWRITE);
+
if (!enable)
return;
@@ -298,6 +303,7 @@ static void dwc2_release_channel_ddma(st
hsotg->non_periodic_channels--;
} else {
dwc2_update_frame_list(hsotg, qh, 0);
+ hsotg->available_host_channels++;
}
/*
@@ -380,6 +386,8 @@ err0:
*/
void dwc2_hcd_qh_free_ddma(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
{
+ unsigned long flags;
+
dwc2_desc_list_free(hsotg, qh);
/*
@@ -389,8 +397,10 @@ void dwc2_hcd_qh_free_ddma(struct dwc2_h
* when it comes here from endpoint disable routine
* channel remains assigned.
*/
+ spin_lock_irqsave(&hsotg->lock, flags);
if (qh->channel)
dwc2_release_channel_ddma(hsotg, qh);
+ spin_unlock_irqrestore(&hsotg->lock, flags);
if ((qh->ep_type == USB_ENDPOINT_XFER_ISOC ||
qh->ep_type == USB_ENDPOINT_XFER_INT) &&
@@ -544,14 +554,22 @@ static void dwc2_fill_host_isoc_dma_desc
dma_desc->status = qh->n_bytes[idx] << HOST_DMA_ISOC_NBYTES_SHIFT &
HOST_DMA_ISOC_NBYTES_MASK;
+ /* Set active bit */
+ dma_desc->status |= HOST_DMA_A;
+
+ qh->ntd++;
+ qtd->isoc_frame_index_last++;
+
#ifdef ISOC_URB_GIVEBACK_ASAP
/* Set IOC for each descriptor corresponding to last frame of URB */
if (qtd->isoc_frame_index_last == qtd->urb->packet_count)
dma_desc->status |= HOST_DMA_IOC;
#endif
- qh->ntd++;
- qtd->isoc_frame_index_last++;
+ usb_syncmem(&qh->desc_list_usbdma,
+ (idx * sizeof(struct dwc2_hcd_dma_desc)),
+ sizeof(struct dwc2_hcd_dma_desc),
+ BUS_DMASYNC_PREWRITE);
}
static void dwc2_init_isoc_dma_desc(struct dwc2_hsotg *hsotg,
@@ -559,11 +577,32 @@ static void dwc2_init_isoc_dma_desc(stru
{
struct dwc2_qtd *qtd;
u32 max_xfer_size;
- u16 idx, inc, n_desc, ntd_max = 0;
+ u16 idx, inc, n_desc = 0, ntd_max = 0;
+ u16 cur_idx;
+ u16 next_idx;
idx = qh->td_last;
inc = qh->interval;
- n_desc = 0;
+ hsotg->frame_number = dwc2_hcd_get_frame_number(hsotg);
+ cur_idx = dwc2_frame_list_idx(hsotg->frame_number);
+ next_idx = dwc2_desclist_idx_inc(qh->td_last, inc, qh->dev_speed);
+
+ /*
+ * Ensure current frame number didn't overstep last scheduled
+ * descriptor. If it happens, the only way to recover is to move
+ * qh->td_last to current frame number + 1.
+ * So that next isoc descriptor will be scheduled on frame number + 1
+ * and not on a past frame.
+ */
+ if (dwc2_frame_idx_num_gt(cur_idx, next_idx) || (cur_idx == next_idx)) {
+ if (inc < 32) {
+ dev_vdbg(hsotg->dev,
+ "current frame number overstep last descriptor\n");
+ qh->td_last = dwc2_desclist_idx_inc(cur_idx, inc,
+ qh->dev_speed);
+ idx = qh->td_last;
+ }
+ }
if (qh->interval) {
ntd_max = (dwc2_max_desc_num(qh) + qh->interval - 1) /
@@ -576,15 +615,20 @@ static void dwc2_init_isoc_dma_desc(stru
MAX_ISOC_XFER_SIZE_HS : MAX_ISOC_XFER_SIZE_FS;
list_for_each_entry(qtd, &qh->qtd_list, qtd_list_entry) {
+ if (qtd->in_process &&
+ qtd->isoc_frame_index_last ==
+ qtd->urb->packet_count)
+ continue;
+
+ qtd->isoc_td_first = idx;
while (qh->ntd < ntd_max && qtd->isoc_frame_index_last <
qtd->urb->packet_count) {
- if (n_desc > 1)
- qh->desc_list[n_desc - 1].status |= HOST_DMA_A;
dwc2_fill_host_isoc_dma_desc(hsotg, qtd, qh,
max_xfer_size, idx);
idx = dwc2_desclist_idx_inc(idx, inc, qh->dev_speed);
n_desc++;
}
+ qtd->isoc_td_last = idx;
qtd->in_process = 1;
}
@@ -595,6 +639,11 @@ static void dwc2_init_isoc_dma_desc(stru
if (qh->ntd == ntd_max) {
idx = dwc2_desclist_idx_dec(qh->td_last, inc, qh->dev_speed);
qh->desc_list[idx].status |= HOST_DMA_IOC;
+
+ usb_syncmem(&qh->desc_list_usbdma,
+ (idx * sizeof(struct dwc2_hcd_dma_desc)),
+ sizeof(struct dwc2_hcd_dma_desc),
+ BUS_DMASYNC_PREWRITE);
}
#else
/*
@@ -624,13 +673,11 @@ static void dwc2_init_isoc_dma_desc(stru
idx = dwc2_desclist_idx_dec(qh->td_last, inc, qh->dev_speed);
qh->desc_list[idx].status |= HOST_DMA_IOC;
+ usb_syncmem(&qh->desc_list_usbdma,
+ (idx * sizeof(struct dwc2_hcd_dma_desc)),
+ sizeof(struct dwc2_hcd_dma_desc),
+ BUS_DMASYNC_PREWRITE);
#endif
-
- if (n_desc) {
- qh->desc_list[n_desc - 1].status |= HOST_DMA_A;
- if (n_desc > 1)
- qh->desc_list[0].status |= HOST_DMA_A;
- }
}
static void dwc2_fill_host_dma_desc(struct dwc2_hsotg *hsotg,
@@ -667,6 +714,11 @@ static void dwc2_fill_host_dma_desc(stru
dma_desc->buf = (u32)chan->xfer_dma;
+ usb_syncmem(&qh->desc_list_usbdma,
+ (n_desc * sizeof(struct dwc2_hcd_dma_desc)),
+ sizeof(struct dwc2_hcd_dma_desc),
+ BUS_DMASYNC_PREWRITE);
+
/*
* Last (or only) descriptor of IN transfer with actual size less
* than MaxPacket
@@ -717,6 +769,11 @@ static void dwc2_init_non_isoc_dma_desc(
"set A bit in desc %d (%p)\n",
n_desc - 1,
&qh->desc_list[n_desc - 1]);
+ usb_syncmem(&qh->desc_list_usbdma,
+ ((n_desc - 1) *
+ sizeof(struct dwc2_hcd_dma_desc)),
+ sizeof(struct dwc2_hcd_dma_desc),
+ BUS_DMASYNC_PREWRITE);
}
dwc2_fill_host_dma_desc(hsotg, chan, qtd, qh, n_desc);
dev_vdbg(hsotg->dev,
@@ -742,10 +799,17 @@ static void dwc2_init_non_isoc_dma_desc(
HOST_DMA_IOC | HOST_DMA_EOL | HOST_DMA_A;
dev_vdbg(hsotg->dev, "set IOC/EOL/A bits in desc %d (%p)\n",
n_desc - 1, &qh->desc_list[n_desc - 1]);
+ usb_syncmem(&qh->desc_list_usbdma,
+ ((n_desc - 1) * sizeof(struct dwc2_hcd_dma_desc)),
+ sizeof(struct dwc2_hcd_dma_desc),
+ BUS_DMASYNC_PREWRITE);
if (n_desc > 1) {
qh->desc_list[0].status |= HOST_DMA_A;
dev_vdbg(hsotg->dev, "set A bit in desc 0 (%p)\n",
&qh->desc_list[0]);
+ usb_syncmem(&qh->desc_list_usbdma, 0,
+ sizeof(struct dwc2_hcd_dma_desc),
+ BUS_DMASYNC_PREWRITE);
}
chan->ntd = n_desc;
}
@@ -820,7 +884,7 @@ static int dwc2_cmpl_host_isoc_dma_desc(
struct dwc2_qtd *qtd,
struct dwc2_qh *qh, u16 idx)
{
- struct dwc2_hcd_dma_desc *dma_desc = &qh->desc_list[idx];
+ struct dwc2_hcd_dma_desc *dma_desc;
struct dwc2_hcd_iso_packet_desc *frame_desc;
u16 remain = 0;
int rc = 0;
@@ -828,6 +892,13 @@ static int dwc2_cmpl_host_isoc_dma_desc(
if (!qtd->urb)
return -EINVAL;
+ usb_syncmem(&qh->desc_list_usbdma,
+ (idx * sizeof(struct dwc2_hcd_dma_desc)),
+ sizeof(struct dwc2_hcd_dma_desc),
+ BUS_DMASYNC_POSTREAD);
+
+ dma_desc = &qh->desc_list[idx];
+
frame_desc = &qtd->urb->iso_descs[qtd->isoc_frame_index_last];
dma_desc->buf = (u32)(DMAADDR(qtd->urb->usbdma, frame_desc->offset));
if (chan->ep_is_in)
@@ -931,17 +1002,51 @@ static void dwc2_complete_isoc_xfer_ddma
list_for_each_entry_safe(qtd, qtd_tmp, &qh->qtd_list, qtd_list_entry) {
if (!qtd->in_process)
break;
+
+ /*
+ * Ensure idx corresponds to descriptor where first urb of this
+ * qtd was added. In fact, during isoc desc init, dwc2 may skip
+ * an index if current frame number is already over this index.
+ */
+ if (idx != qtd->isoc_td_first) {
+ dev_vdbg(hsotg->dev,
+ "try to complete %d instead of %d\n",
+ idx, qtd->isoc_td_first);
+ idx = qtd->isoc_td_first;
+ }
+
do {
+ struct dwc2_qtd *qtd_next;
+ u16 cur_idx;
+
rc = dwc2_cmpl_host_isoc_dma_desc(hsotg, chan, qtd, qh,
idx);
if (rc < 0)
return;
idx = dwc2_desclist_idx_inc(idx, qh->interval,
chan->speed);
- if (rc == DWC2_CMPL_STOP)
- goto stop_scan;
+ if (!rc)
+ continue;
+
if (rc == DWC2_CMPL_DONE)
break;
+
+ /* rc == DWC2_CMPL_STOP */
+
+ if (qh->interval >= 32)
+ goto stop_scan;
+
+ qh->td_first = idx;
+ cur_idx = dwc2_frame_list_idx(hsotg->frame_number);
+ qtd_next = list_first_entry(&qh->qtd_list,
+ struct dwc2_qtd,
+ qtd_list_entry);
+ if (dwc2_frame_idx_num_gt(cur_idx,
+ qtd_next->isoc_td_last))
+ break;
+
+ goto stop_scan;
+
} while (idx != qh->td_first);
}
@@ -1049,6 +1154,11 @@ static int dwc2_process_non_isoc_desc(st
if (!urb)
return -EINVAL;
+ usb_syncmem(&qh->desc_list_usbdma,
+ (desc_num * sizeof(struct dwc2_hcd_dma_desc)),
+ sizeof(struct dwc2_hcd_dma_desc),
+ BUS_DMASYNC_POSTREAD);
+
dma_desc = &qh->desc_list[desc_num];
n_bytes = qh->n_bytes[desc_num];
dev_vdbg(hsotg->dev,
@@ -1057,7 +1167,10 @@ static int dwc2_process_non_isoc_desc(st
failed = dwc2_update_non_isoc_urb_state_ddma(hsotg, chan, qtd, dma_desc,
halt_status, n_bytes,
xfer_done);
- if (failed || (*xfer_done && urb->status != -EINPROGRESS)) {
+ if (*xfer_done && urb->status != -EINPROGRESS)
+ failed = 1;
+
+ if (failed) {
dwc2_host_complete(hsotg, qtd, urb->status);
dwc2_hcd_qtd_unlink_and_free(hsotg, qtd, qh);
dev_vdbg(hsotg->dev, "failed=%1x xfer_done=%1x status=%08x\n",
@@ -1185,6 +1298,21 @@ void dwc2_hcd_complete_xfer_ddma(struct
/* Release the channel if halted or session completed */
if (halt_status != DWC2_HC_XFER_COMPLETE ||
list_empty(&qh->qtd_list)) {
+ struct dwc2_qtd *qtd, *qtd_tmp;
+
+ /*
+ * Kill all remainings QTDs since channel has been
+ * halted.
+ */
+ list_for_each_entry_safe(qtd, qtd_tmp,
+ &qh->qtd_list,
+ qtd_list_entry) {
+ dwc2_host_complete(hsotg, qtd,
+ -ECONNRESET);
+ dwc2_hcd_qtd_unlink_and_free(hsotg,
+ qtd, qh);
+ }
+
/* Halt the channel if session completed */
if (halt_status == DWC2_HC_XFER_COMPLETE)
dwc2_hc_halt(hsotg, chan, halt_status);
@@ -1194,7 +1322,12 @@ void dwc2_hcd_complete_xfer_ddma(struct
/* Keep in assigned schedule to continue transfer */
list_move(&qh->qh_list_entry,
&hsotg->periodic_sched_assigned);
- continue_isoc_xfer = 1;
+ /*
+ * If channel has been halted during giveback of urb
+ * then prevent any new scheduling.
+ */
+ if (!chan->halt_status)
+ continue_isoc_xfer = 1;
}
/*
* Todo: Consider the case when period exceeds FrameList size.
Index: src/sys/external/bsd/dwc2/dist/dwc2_coreintr.c
diff -u src/sys/external/bsd/dwc2/dist/dwc2_coreintr.c:1.9 src/sys/external/bsd/dwc2/dist/dwc2_coreintr.c:1.10
--- src/sys/external/bsd/dwc2/dist/dwc2_coreintr.c:1.9 Sun Aug 30 12:59:59 2015
+++ src/sys/external/bsd/dwc2/dist/dwc2_coreintr.c Sun Feb 14 10:53:30 2016
@@ -1,4 +1,4 @@
-/* $NetBSD: dwc2_coreintr.c,v 1.9 2015/08/30 12:59:59 skrll Exp $ */
+/* $NetBSD: dwc2_coreintr.c,v 1.10 2016/02/14 10:53:30 skrll Exp $ */
/*
* core_intr.c - DesignWare HS OTG Controller common interrupt handling
@@ -41,7 +41,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: dwc2_coreintr.c,v 1.9 2015/08/30 12:59:59 skrll Exp $");
+__KERNEL_RCSID(0, "$NetBSD: dwc2_coreintr.c,v 1.10 2016/02/14 10:53:30 skrll Exp $");
#include <sys/param.h>
#include <sys/kernel.h>
@@ -100,9 +100,6 @@ static void dwc2_handle_usb_port_intr(st
hprt0 &= ~HPRT0_ENA;
DWC2_WRITE_4(hsotg, HPRT0, hprt0);
}
-
- /* Clear interrupt */
- DWC2_WRITE_4(hsotg, GINTSTS, GINTSTS_PRTINT);
}
/**
@@ -112,11 +109,11 @@ static void dwc2_handle_usb_port_intr(st
*/
static void dwc2_handle_mode_mismatch_intr(struct dwc2_hsotg *hsotg)
{
- dev_warn(hsotg->dev, "Mode Mismatch Interrupt: currently in %s mode\n",
- dwc2_is_host_mode(hsotg) ? "Host" : "Device");
-
/* Clear interrupt */
DWC2_WRITE_4(hsotg, GINTSTS, GINTSTS_MODEMIS);
+
+ dev_warn(hsotg->dev, "Mode Mismatch Interrupt: currently in %s mode\n",
+ dwc2_is_host_mode(hsotg) ? "Host" : "Device");
}
/**
@@ -143,7 +140,7 @@ static void dwc2_handle_otg_intr(struct
gotgctl = DWC2_READ_4(hsotg, GOTGCTL);
if (dwc2_is_device_mode(hsotg))
- s3c_hsotg_disconnect(hsotg);
+ dwc2_hsotg_disconnect(hsotg);
if (hsotg->op_state == OTG_STATE_B_HOST) {
hsotg->op_state = OTG_STATE_B_PERIPHERAL;
@@ -253,7 +250,7 @@ static void dwc2_handle_otg_intr(struct
dev_dbg(hsotg->dev, "a_suspend->a_peripheral (%d)\n",
hsotg->op_state);
spin_unlock(&hsotg->lock);
- dwc2_hcd_disconnect(hsotg);
+ dwc2_hcd_disconnect(hsotg, false);
spin_lock(&hsotg->lock);
hsotg->op_state = OTG_STATE_A_PERIPHERAL;
} else {
@@ -290,9 +287,13 @@ static void dwc2_handle_otg_intr(struct
*/
static void dwc2_handle_conn_id_status_change_intr(struct dwc2_hsotg *hsotg)
{
- u32 gintmsk = DWC2_READ_4(hsotg, GINTMSK);
+ u32 gintmsk;
+
+ /* Clear interrupt */
+ DWC2_WRITE_4(hsotg, GINTSTS, GINTSTS_CONIDSTSCHNG);
/* Need to disable SOF interrupt immediately */
+ gintmsk = DWC2_READ_4(hsotg, GINTMSK);
gintmsk &= ~GINTSTS_SOF;
DWC2_WRITE_4(hsotg, GINTMSK, gintmsk);
@@ -309,9 +310,6 @@ static void dwc2_handle_conn_id_status_c
workqueue_enqueue(hsotg->wq_otg, &hsotg->wf_otg, NULL);
spin_lock(&hsotg->lock);
}
-
- /* Clear interrupt */
- DWC2_WRITE_4(hsotg, GINTSTS, GINTSTS_CONIDSTSCHNG);
}
/**
@@ -327,16 +325,28 @@ static void dwc2_handle_conn_id_status_c
*/
static void dwc2_handle_session_req_intr(struct dwc2_hsotg *hsotg)
{
- dev_dbg(hsotg->dev, "++Session Request Interrupt++\n");
+ int ret;
/* Clear interrupt */
DWC2_WRITE_4(hsotg, GINTSTS, GINTSTS_SESSREQINT);
- /*
- * Report disconnect if there is any previous session established
- */
- if (dwc2_is_device_mode(hsotg))
- s3c_hsotg_disconnect(hsotg);
+ dev_dbg(hsotg->dev, "Session request interrupt - lx_state=%d\n",
+ hsotg->lx_state);
+
+ if (dwc2_is_device_mode(hsotg)) {
+ if (hsotg->lx_state == DWC2_L2) {
+ ret = dwc2_exit_hibernation(hsotg, true);
+ if (ret && (ret != -ENOTSUPP))
+ dev_err(hsotg->dev,
+ "exit hibernation failed\n");
+ }
+
+ /*
+ * Report disconnect if there is any previous session
+ * established
+ */
+ dwc2_hsotg_disconnect(hsotg);
+ }
}
/*
@@ -349,6 +359,10 @@ static void dwc2_handle_session_req_intr
static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg)
{
int ret;
+
+ /* Clear interrupt */
+ DWC2_WRITE_4(hsotg, GINTSTS, GINTSTS_WKUPINT);
+
dev_dbg(hsotg->dev, "++Resume or Remote Wakeup Detected Interrupt++\n");
dev_dbg(hsotg->dev, "%s lxstate = %d\n", __func__, hsotg->lx_state);
@@ -369,6 +383,9 @@ static void dwc2_handle_wakeup_detected_
/* Change to L0 state */
hsotg->lx_state = DWC2_L0;
} else {
+ if (hsotg->core_params->hibernation)
+ return;
+
if (hsotg->lx_state != DWC2_L1) {
u32 pcgcctl = DWC2_READ_4(hsotg, PCGCTL);
@@ -382,9 +399,6 @@ static void dwc2_handle_wakeup_detected_
hsotg->lx_state = DWC2_L0;
}
}
-
- /* Clear interrupt */
- DWC2_WRITE_4(hsotg, GINTSTS, GINTSTS_WKUPINT);
}
/*
@@ -393,17 +407,14 @@ static void dwc2_handle_wakeup_detected_
*/
static void dwc2_handle_disconnect_intr(struct dwc2_hsotg *hsotg)
{
+ DWC2_WRITE_4(hsotg, GINTSTS, GINTSTS_DISCONNINT);
+
dev_dbg(hsotg->dev, "++Disconnect Detected Interrupt++ (%s) %s\n",
dwc2_is_host_mode(hsotg) ? "Host" : "Device",
dwc2_op_state_str(hsotg));
if (hsotg->op_state == OTG_STATE_A_HOST)
- dwc2_hcd_disconnect(hsotg);
-
- /* Change to L3 (OFF) state */
- hsotg->lx_state = DWC2_L3;
-
- DWC2_WRITE_4(hsotg, GINTSTS, GINTSTS_DISCONNINT);
+ dwc2_hcd_disconnect(hsotg, false);
}
/*
@@ -419,6 +430,9 @@ static void dwc2_handle_usb_suspend_intr
u32 dsts;
int ret;
+ /* Clear interrupt */
+ DWC2_WRITE_4(hsotg, GINTSTS, GINTSTS_USBSUSP);
+
dev_dbg(hsotg->dev, "USB SUSPEND\n");
if (dwc2_is_device_mode(hsotg)) {
@@ -437,7 +451,7 @@ static void dwc2_handle_usb_suspend_intr
if (!dwc2_is_device_connected(hsotg)) {
dev_dbg(hsotg->dev,
"ignore suspend request before enumeration\n");
- goto clear_int;
+ return;
}
ret = dwc2_enter_hibernation(hsotg);
@@ -476,10 +490,6 @@ skip_power_saving:
hsotg->op_state = OTG_STATE_A_HOST;
}
}
-
-clear_int:
- /* Clear interrupt */
- DWC2_WRITE_4(hsotg, GINTSTS, GINTSTS_USBSUSP);
}
#define GINTMSK_COMMON (GINTSTS_WKUPINT | GINTSTS_SESSREQINT | \
Index: src/sys/external/bsd/dwc2/dist/dwc2_hcd.c
diff -u src/sys/external/bsd/dwc2/dist/dwc2_hcd.c:1.17 src/sys/external/bsd/dwc2/dist/dwc2_hcd.c:1.18
--- src/sys/external/bsd/dwc2/dist/dwc2_hcd.c:1.17 Tue Dec 22 14:29:28 2015
+++ src/sys/external/bsd/dwc2/dist/dwc2_hcd.c Sun Feb 14 10:53:30 2016
@@ -1,4 +1,4 @@
-/* $NetBSD: dwc2_hcd.c,v 1.17 2015/12/22 14:29:28 skrll Exp $ */
+/* $NetBSD: dwc2_hcd.c,v 1.18 2016/02/14 10:53:30 skrll Exp $ */
/*
* hcd.c - DesignWare HS OTG Controller host-mode routines
@@ -42,7 +42,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: dwc2_hcd.c,v 1.17 2015/12/22 14:29:28 skrll Exp $");
+__KERNEL_RCSID(0, "$NetBSD: dwc2_hcd.c,v 1.18 2016/02/14 10:53:30 skrll Exp $");
#include <sys/types.h>
#include <sys/kmem.h>
@@ -145,7 +145,7 @@ static void dwc2_kill_urbs_in_qh_list(st
list_for_each_entry_safe(qh, qh_tmp, qh_list, qh_list_entry) {
list_for_each_entry_safe(qtd, qtd_tmp, &qh->qtd_list,
qtd_list_entry) {
- dwc2_host_complete(hsotg, qtd, -ETIMEDOUT);
+ dwc2_host_complete(hsotg, qtd, -ECONNRESET);
dwc2_hcd_qtd_unlink_and_free(hsotg, qtd, qh);
}
}
@@ -279,15 +279,33 @@ static void dwc2_hcd_cleanup_channels(st
}
/**
+ * dwc2_hcd_connect() - Handles connect of the HCD
+ *
+ * @hsotg: Pointer to struct dwc2_hsotg
+ *
+ * Must be called with interrupt disabled and spinlock held
+ */
+void dwc2_hcd_connect(struct dwc2_hsotg *hsotg)
+{
+ if (hsotg->lx_state != DWC2_L0)
+ usb_hcd_resume_root_hub(hsotg->priv);
+
+ hsotg->flags.b.port_connect_status_change = 1;
+ hsotg->flags.b.port_connect_status = 1;
+}
+
+/**
* dwc2_hcd_disconnect() - Handles disconnect of the HCD
*
* @hsotg: Pointer to struct dwc2_hsotg
+ * @force: If true, we won't try to reconnect even if we see device connected.
*
* Must be called with interrupt disabled and spinlock held
*/
-void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg)
+void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg, bool force)
{
u32 intr;
+ u32 hprt0;
/* Set status flags for the hub driver */
hsotg->flags.b.port_connect_status_change = 1;
@@ -328,6 +346,24 @@ void dwc2_hcd_disconnect(struct dwc2_hso
dwc2_host_disconnect(hsotg);
dwc2_root_intr(hsotg->hsotg_sc);
+
+ /*
+ * Add an extra check here to see if we're actually connected but
+ * we don't have a detection interrupt pending. This can happen if:
+ * 1. hardware sees connect
+ * 2. hardware sees disconnect
+ * 3. hardware sees connect
+ * 4. dwc2_port_intr() - clears connect interrupt
+ * 5. dwc2_handle_common_intr() - calls here
+ *
+ * Without the extra check here we will end calling disconnect
+ * and won't get any future interrupts to handle the connect.
+ */
+ if (!force) {
+ hprt0 = DWC2_READ_4(hsotg, HPRT0);
+ if (!(hprt0 & HPRT0_CONNDET) && (hprt0 & HPRT0_CONNSTS))
+ dwc2_hcd_connect(hsotg);
+ }
}
/**
@@ -337,13 +373,14 @@ void dwc2_hcd_disconnect(struct dwc2_hso
*/
static void dwc2_hcd_rem_wakeup(struct dwc2_hsotg *hsotg)
{
- if (hsotg->lx_state == DWC2_L2) {
+ if (hsotg->bus_suspended) {
hsotg->flags.b.port_suspend_change = 1;
usb_hcd_resume_root_hub(hsotg->priv);
- } else {
- hsotg->flags.b.port_l1_change = 1;
}
+ if (hsotg->lx_state == DWC2_L1)
+ hsotg->flags.b.port_l1_change = 1;
+
dwc2_root_intr(hsotg->hsotg_sc);
}
@@ -840,8 +877,11 @@ static int dwc2_assign_and_init_hc(struc
*/
chan->multi_count = dwc2_hb_mult(qh->maxp);
- if (hsotg->core_params->dma_desc_enable > 0)
+ if (hsotg->core_params->dma_desc_enable > 0) {
+ chan->desc_list_usbdma = qh->desc_list_usbdma;
chan->desc_list_addr = qh->desc_list_dma;
+ chan->desc_list_sz = qh->desc_list_sz;
+ }
dwc2_hc_init(hsotg, chan);
chan->qh = qh;
@@ -1332,6 +1372,7 @@ dwc2_conn_id_status_change(struct work *
wf_otg);
u32 count = 0;
u32 gotgctl;
+ unsigned long flags;
dev_dbg(hsotg->dev, "%s()\n", __func__);
@@ -1359,8 +1400,10 @@ dwc2_conn_id_status_change(struct work *
hsotg->op_state = OTG_STATE_B_PERIPHERAL;
dwc2_core_init(hsotg, false);
dwc2_enable_global_interrupts(hsotg);
- s3c_hsotg_core_init_disconnected(hsotg, false);
- s3c_hsotg_core_connect(hsotg);
+ spin_lock_irqsave(&hsotg->lock, flags);
+ dwc2_hsotg_core_init_disconnected(hsotg, false);
+ spin_unlock_irqrestore(&hsotg->lock, flags);
+ dwc2_hsotg_core_connect(hsotg);
} else {
/* A-Device connector (Host Mode) */
dev_dbg(hsotg->dev, "connId A\n");
@@ -1403,6 +1446,7 @@ void dwc2_wakeup_detected(void * data)
DWC2_READ_4(hsotg, HPRT0));
dwc2_hcd_rem_wakeup(hsotg);
+ hsotg->bus_suspended = 0;
/* Change to L0 state */
hsotg->lx_state = DWC2_L0;
@@ -1431,14 +1475,19 @@ static void dwc2_port_suspend(struct dwc
hprt0 |= HPRT0_SUSP;
DWC2_WRITE_4(hsotg, HPRT0, hprt0);
- /* Update lx_state */
- hsotg->lx_state = DWC2_L2;
+ hsotg->bus_suspended = 1;
- /* Suspend the Phy Clock */
- pcgctl = DWC2_READ_4(hsotg, PCGCTL);
- pcgctl |= PCGCTL_STOPPCLK;
- DWC2_WRITE_4(hsotg, PCGCTL, pcgctl);
- udelay(10);
+ /*
+ * If hibernation is supported, Phy clock will be suspended
+ * after registers are backuped.
+ */
+ if (!hsotg->core_params->hibernation) {
+ /* Suspend the Phy Clock */
+ pcgctl = DWC2_READ_4(hsotg, PCGCTL);
+ pcgctl |= PCGCTL_STOPPCLK;
+ DWC2_WRITE_4(hsotg, PCGCTL, pcgctl);
+ udelay(10);
+ }
/* For HNP the bus must be suspended for at least 200ms */
if (dwc2_host_is_b_hnp_enabled(hsotg)) {
@@ -1454,6 +1503,44 @@ static void dwc2_port_suspend(struct dwc
}
}
+/* Must NOT be called with interrupt disabled or spinlock held */
+static void dwc2_port_resume(struct dwc2_hsotg *hsotg)
+{
+ unsigned long flags;
+ u32 hprt0;
+ u32 pcgctl;
+
+ spin_lock_irqsave(&hsotg->lock, flags);
+
+ /*
+ * If hibernation is supported, Phy clock is already resumed
+ * after registers restore.
+ */
+ if (!hsotg->core_params->hibernation) {
+ pcgctl = DWC2_READ_4(hsotg, PCGCTL);
+ pcgctl &= ~PCGCTL_STOPPCLK;
+ DWC2_WRITE_4(hsotg, PCGCTL, pcgctl);
+ spin_unlock_irqrestore(&hsotg->lock, flags);
+ usleep_range(20000, 40000);
+ spin_lock_irqsave(&hsotg->lock, flags);
+ }
+
+ hprt0 = dwc2_read_hprt0(hsotg);
+ hprt0 |= HPRT0_RES;
+ hprt0 &= ~HPRT0_SUSP;
+ DWC2_WRITE_4(hsotg, HPRT0, hprt0);
+ spin_unlock_irqrestore(&hsotg->lock, flags);
+
+ msleep(USB_RESUME_TIMEOUT);
+
+ spin_lock_irqsave(&hsotg->lock, flags);
+ hprt0 = dwc2_read_hprt0(hsotg);
+ hprt0 &= ~(HPRT0_RES | HPRT0_SUSP);
+ DWC2_WRITE_4(hsotg, HPRT0, hprt0);
+ hsotg->bus_suspended = 0;
+ spin_unlock_irqrestore(&hsotg->lock, flags);
+}
+
/* Handles hub class-specific requests */
int
dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
@@ -1501,17 +1588,8 @@ dwc2_hcd_hub_control(struct dwc2_hsotg *
case USB_PORT_FEAT_SUSPEND:
dev_dbg(hsotg->dev,
"ClearPortFeature USB_PORT_FEAT_SUSPEND\n");
- DWC2_WRITE_4(hsotg, PCGCTL, 0);
- usleep_range(20000, 40000);
-
- hprt0 = dwc2_read_hprt0(hsotg);
- hprt0 |= HPRT0_RES;
- DWC2_WRITE_4(hsotg, HPRT0, hprt0);
- hprt0 &= ~HPRT0_SUSP;
- msleep(USB_RESUME_TIMEOUT);
-
- hprt0 &= ~HPRT0_RES;
- DWC2_WRITE_4(hsotg, HPRT0, hprt0);
+ if (hsotg->bus_suspended)
+ dwc2_port_resume(hsotg);
break;
case USB_PORT_FEAT_POWER:
@@ -1670,6 +1748,27 @@ dwc2_hcd_hub_control(struct dwc2_hsotg *
/* USB_PORT_FEAT_INDICATOR unsupported always 0 */
USETW(ps.wPortStatus, port_status);
+ if (hsotg->core_params->dma_desc_fs_enable) {
+ /*
+ * Enable descriptor DMA only if a full speed
+ * device is connected.
+ */
+ if (hsotg->new_connection &&
+ ((port_status &
+ (USB_PORT_STAT_CONNECTION |
+ USB_PORT_STAT_HIGH_SPEED |
+ USB_PORT_STAT_LOW_SPEED)) ==
+ USB_PORT_STAT_CONNECTION)) {
+ u32 hcfg;
+
+ dev_info(hsotg->dev, "Enabling descriptor DMA mode\n");
+ hsotg->core_params->dma_desc_enable = 1;
+ hcfg = DWC2_READ_4(hsotg, HCFG);
+ hcfg |= HCFG_DESCDMA;
+ DWC2_WRITE_4(hsotg, HCFG, hcfg);
+ hsotg->new_connection = false;
+ }
+ }
dev_vdbg(hsotg->dev, "wPortStatus=%04x\n", port_status);
memcpy(buf, &ps, sizeof(ps));
break;
@@ -2055,7 +2154,6 @@ void dwc2_host_disconnect(struct dwc2_hs
// hcd->self.is_b_host = 0;
}
-
/*
* Work queue function for starting the HCD when A-Cable is connected
*/
@@ -2077,15 +2175,21 @@ dwc2_hcd_reset_func(struct work *work)
{
struct dwc2_hsotg *hsotg = container_of(work, struct dwc2_hsotg,
reset_work.work);
+ unsigned long flags;
u32 hprt0;
dev_dbg(hsotg->dev, "USB RESET function called\n");
+
+ spin_lock_irqsave(&hsotg->lock, flags);
+
hprt0 = dwc2_read_hprt0(hsotg);
hprt0 &= ~HPRT0_RST;
DWC2_WRITE_4(hsotg, HPRT0, hprt0);
hsotg->flags.b.port_reset_change = 1;
dwc2_root_intr(hsotg->hsotg_sc);
+
+ spin_unlock_irqrestore(&hsotg->lock, flags);
}
/*
@@ -2342,6 +2446,7 @@ void dwc2_hcd_remove(struct dwc2_hsotg *
return;
}
hsotg->priv = NULL;
+
dwc2_hcd_release(hsotg);
#ifdef CONFIG_USB_DWC2_TRACK_MISSED_SOFS
Index: src/sys/external/bsd/dwc2/dist/dwc2_hcd.h
diff -u src/sys/external/bsd/dwc2/dist/dwc2_hcd.h:1.11 src/sys/external/bsd/dwc2/dist/dwc2_hcd.h:1.12
--- src/sys/external/bsd/dwc2/dist/dwc2_hcd.h:1.11 Sun Feb 14 10:34:09 2016
+++ src/sys/external/bsd/dwc2/dist/dwc2_hcd.h Sun Feb 14 10:53:30 2016
@@ -1,4 +1,4 @@
-/* $NetBSD: dwc2_hcd.h,v 1.11 2016/02/14 10:34:09 skrll Exp $ */
+/* $NetBSD: dwc2_hcd.h,v 1.12 2016/02/14 10:53:30 skrll Exp $ */
/*
* hcd.h - DesignWare HS OTG Controller host-mode declarations
@@ -109,6 +109,7 @@ struct dwc2_qh;
* @qh: QH for the transfer being processed by this channel
* @hc_list_entry: For linking to list of host channels
* @desc_list_addr: Current QH's descriptor list DMA address
+ * @desc_list_sz: Current QH's descriptor list size
*
* This structure represents the state of a single host channel when acting in
* host mode. It contains the data items needed to transfer packets to an
@@ -161,7 +162,9 @@ struct dwc2_host_chan {
u32 hcint;
struct dwc2_qh *qh;
struct list_head hc_list_entry;
+ usb_dma_t desc_list_usbdma;
dma_addr_t desc_list_addr;
+ u32 desc_list_sz;
};
struct dwc2_hcd_pipe_info {
@@ -257,6 +260,7 @@ enum dwc2_transaction_type {
* schedule
* @desc_list: List of transfer descriptors
* @desc_list_dma: Physical address of desc_list
+ * @desc_list_sz: Size of descriptors list
* @n_bytes: Xfer Bytes array. Each element corresponds to a transfer
* descriptor and indicates original XferSize value for the
* descriptor
@@ -293,6 +297,7 @@ struct dwc2_qh {
usb_dma_t desc_list_usbdma;
struct dwc2_hcd_dma_desc *desc_list;
dma_addr_t desc_list_dma;
+ u32 desc_list_sz;
u32 *n_bytes;
unsigned tt_buffer_dirty:1;
};
@@ -349,6 +354,8 @@ struct dwc2_qtd {
u8 isoc_split_pos;
u16 isoc_frame_index;
u16 isoc_split_offset;
+ u16 isoc_td_last;
+ u16 isoc_td_first;
u32 ssplit_out_xfer_count;
u8 error_count;
u8 n_desc;
@@ -387,19 +394,6 @@ static inline void disable_hc_int(struct
}
/*
- * Returns the mode of operation, host or device
- */
-static inline int dwc2_is_host_mode(struct dwc2_hsotg *hsotg)
-{
- return (DWC2_READ_4(hsotg, GINTSTS) & GINTSTS_CURMODE_HOST) != 0;
-}
-
-static inline int dwc2_is_device_mode(struct dwc2_hsotg *hsotg)
-{
- return (DWC2_READ_4(hsotg, GINTSTS) & GINTSTS_CURMODE_HOST) == 0;
-}
-
-/*
* Reads HPRT0 in preparation to modify. It keeps the WC bits 0 so that if they
* are read as 1, they won't clear when written back.
*/
@@ -535,6 +529,19 @@ static inline bool dbg_perio(void) { ret
#define dwc2_max_packet(wmaxpacketsize) ((wmaxpacketsize) & 0x07ff)
/*
+ * Returns true if frame1 index is greater than frame2 index. The comparison
+ * is done modulo FRLISTEN_64_SIZE. This accounts for the rollover of the
+ * frame number when the max index frame number is reached.
+ */
+static inline bool dwc2_frame_idx_num_gt(u16 fr_idx1, u16 fr_idx2)
+{
+ u16 diff = fr_idx1 - fr_idx2;
+ u16 sign = diff & (FRLISTEN_64_SIZE >> 1);
+
+ return diff && !sign;
+}
+
+/*
* Returns true if frame1 is less than or equal to frame2. The comparison is
* done modulo HFNUM_MAX_FRNUM. This accounts for the rollover of the
* frame number when the max frame number is reached.
Index: src/sys/external/bsd/dwc2/dist/dwc2_hcdintr.c
diff -u src/sys/external/bsd/dwc2/dist/dwc2_hcdintr.c:1.12 src/sys/external/bsd/dwc2/dist/dwc2_hcdintr.c:1.13
--- src/sys/external/bsd/dwc2/dist/dwc2_hcdintr.c:1.12 Sun Aug 30 12:59:59 2015
+++ src/sys/external/bsd/dwc2/dist/dwc2_hcdintr.c Sun Feb 14 10:53:30 2016
@@ -1,4 +1,4 @@
-/* $NetBSD: dwc2_hcdintr.c,v 1.12 2015/08/30 12:59:59 skrll Exp $ */
+/* $NetBSD: dwc2_hcdintr.c,v 1.13 2016/02/14 10:53:30 skrll Exp $ */
/*
* hcd_intr.c - DesignWare HS OTG Controller host-mode interrupt handling
@@ -40,7 +40,7 @@
* This file contains the interrupt handlers for Host mode
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: dwc2_hcdintr.c,v 1.12 2015/08/30 12:59:59 skrll Exp $");
+__KERNEL_RCSID(0, "$NetBSD: dwc2_hcdintr.c,v 1.13 2016/02/14 10:53:30 skrll Exp $");
#include <sys/types.h>
#include <sys/pool.h>
@@ -125,6 +125,9 @@ static void dwc2_sof_intr(struct dwc2_hs
struct dwc2_qh *qh;
enum dwc2_transaction_type tr_type;
+ /* Clear interrupt */
+ DWC2_WRITE_4(hsotg, GINTSTS, GINTSTS_SOF);
+
#ifdef DEBUG_SOF
dev_vdbg(hsotg->dev, "--Start of Frame Interrupt--\n");
#endif
@@ -149,9 +152,6 @@ static void dwc2_sof_intr(struct dwc2_hs
tr_type = dwc2_hcd_select_transactions(hsotg);
if (tr_type != DWC2_TRANSACTION_NONE)
dwc2_hcd_queue_transactions(hsotg, tr_type);
-
- /* Clear interrupt */
- DWC2_WRITE_4(hsotg, GINTSTS, GINTSTS_SOF);
}
/*
@@ -317,6 +317,7 @@ static void dwc2_hprt0_enable(struct dwc
if (do_reset) {
*hprt0_modify |= HPRT0_RST;
+ DWC2_WRITE_4(hsotg, HPRT0, *hprt0_modify);
queue_delayed_work(hsotg->wq_otg, &hsotg->reset_work,
msecs_to_jiffies(60));
} else {
@@ -354,15 +355,12 @@ static void dwc2_port_intr(struct dwc2_h
* Set flag and clear if detected
*/
if (hprt0 & HPRT0_CONNDET) {
+ DWC2_WRITE_4(hsotg, HPRT0, hprt0_modify | HPRT0_CONNDET);
+
dev_vdbg(hsotg->dev,
"--Port Interrupt HPRT0=0x%08x Port Connect Detected--\n",
hprt0);
- if (hsotg->lx_state != DWC2_L0)
- usb_hcd_resume_root_hub(hsotg->priv);
-
- hsotg->flags.b.port_connect_status_change = 1;
- hsotg->flags.b.port_connect_status = 1;
- hprt0_modify |= HPRT0_CONNDET;
+ dwc2_hcd_connect(hsotg);
/*
* The Hub driver asserts a reset when it sees port connect
@@ -375,28 +373,36 @@ static void dwc2_port_intr(struct dwc2_h
* Clear if detected - Set internal flag if disabled
*/
if (hprt0 & HPRT0_ENACHG) {
+ DWC2_WRITE_4(hsotg, HPRT0, hprt0_modify | HPRT0_ENACHG);
dev_vdbg(hsotg->dev,
" --Port Interrupt HPRT0=0x%08x Port Enable Changed (now %d)--\n",
hprt0, !!(hprt0 & HPRT0_ENA));
- hprt0_modify |= HPRT0_ENACHG;
- if (hprt0 & HPRT0_ENA)
+ if (hprt0 & HPRT0_ENA) {
+ hsotg->new_connection = true;
dwc2_hprt0_enable(hsotg, hprt0, &hprt0_modify);
- else
+ } else {
hsotg->flags.b.port_enable_change = 1;
+ if (hsotg->core_params->dma_desc_fs_enable) {
+ u32 hcfg;
+
+ hsotg->core_params->dma_desc_enable = 0;
+ hsotg->new_connection = false;
+ hcfg = DWC2_READ_4(hsotg, HCFG);
+ hcfg &= ~HCFG_DESCDMA;
+ DWC2_WRITE_4(hsotg, HCFG, hcfg);
+ }
+ }
}
/* Overcurrent Change Interrupt */
if (hprt0 & HPRT0_OVRCURRCHG) {
+ DWC2_WRITE_4(hsotg, HPRT0, hprt0_modify | HPRT0_OVRCURRCHG);
dev_vdbg(hsotg->dev,
" --Port Interrupt HPRT0=0x%08x Port Overcurrent Changed--\n",
hprt0);
hsotg->flags.b.port_over_current_change = 1;
- hprt0_modify |= HPRT0_OVRCURRCHG;
}
- /* Clear Port Interrupts */
- DWC2_WRITE_4(hsotg, HPRT0, hprt0_modify);
-
if (hsotg->flags.b.port_connect_status_change ||
hsotg->flags.b.port_enable_change ||
hsotg->flags.b.port_over_current_change)
@@ -1984,6 +1990,24 @@ static void dwc2_hc_chhltd_intr(struct d
}
}
+/*
+ * Check if the given qtd is still the top of the list (and thus valid).
+ *
+ * If dwc2_hcd_qtd_unlink_and_free() has been called since we grabbed
+ * the qtd from the top of the list, this will return false (otherwise true).
+ */
+static bool dwc2_check_qtd_still_ok(struct dwc2_qtd *qtd, struct dwc2_qh *qh)
+{
+ struct dwc2_qtd *cur_head;
+
+ if (qh == NULL)
+ return false;
+
+ cur_head = list_first_entry(&qh->qtd_list, struct dwc2_qtd,
+ qtd_list_entry);
+ return (cur_head == qtd);
+}
+
/* Handles interrupt for a specific Host Channel */
static void dwc2_hc_n_intr(struct dwc2_hsotg *hsotg, int chnum)
{
@@ -2066,27 +2090,59 @@ static void dwc2_hc_n_intr(struct dwc2_h
*/
hcint &= ~HCINTMSK_NYET;
}
- if (hcint & HCINTMSK_CHHLTD)
+
+ if (hcint & HCINTMSK_CHHLTD) {
dwc2_hc_chhltd_intr(hsotg, chan, chnum, qtd);
- if (hcint & HCINTMSK_AHBERR)
+ if (!dwc2_check_qtd_still_ok(qtd, chan->qh))
+ goto exit;
+ }
+ if (hcint & HCINTMSK_AHBERR) {
dwc2_hc_ahberr_intr(hsotg, chan, chnum, qtd);
- if (hcint & HCINTMSK_STALL)
+ if (!dwc2_check_qtd_still_ok(qtd, chan->qh))
+ goto exit;
+ }
+ if (hcint & HCINTMSK_STALL) {
dwc2_hc_stall_intr(hsotg, chan, chnum, qtd);
- if (hcint & HCINTMSK_NAK)
+ if (!dwc2_check_qtd_still_ok(qtd, chan->qh))
+ goto exit;
+ }
+ if (hcint & HCINTMSK_NAK) {
dwc2_hc_nak_intr(hsotg, chan, chnum, qtd);
- if (hcint & HCINTMSK_ACK)
+ if (!dwc2_check_qtd_still_ok(qtd, chan->qh))
+ goto exit;
+ }
+ if (hcint & HCINTMSK_ACK) {
dwc2_hc_ack_intr(hsotg, chan, chnum, qtd);
- if (hcint & HCINTMSK_NYET)
+ if (!dwc2_check_qtd_still_ok(qtd, chan->qh))
+ goto exit;
+ }
+ if (hcint & HCINTMSK_NYET) {
dwc2_hc_nyet_intr(hsotg, chan, chnum, qtd);
- if (hcint & HCINTMSK_XACTERR)
+ if (!dwc2_check_qtd_still_ok(qtd, chan->qh))
+ goto exit;
+ }
+ if (hcint & HCINTMSK_XACTERR) {
dwc2_hc_xacterr_intr(hsotg, chan, chnum, qtd);
- if (hcint & HCINTMSK_BBLERR)
+ if (!dwc2_check_qtd_still_ok(qtd, chan->qh))
+ goto exit;
+ }
+ if (hcint & HCINTMSK_BBLERR) {
dwc2_hc_babble_intr(hsotg, chan, chnum, qtd);
- if (hcint & HCINTMSK_FRMOVRUN)
+ if (!dwc2_check_qtd_still_ok(qtd, chan->qh))
+ goto exit;
+ }
+ if (hcint & HCINTMSK_FRMOVRUN) {
dwc2_hc_frmovrun_intr(hsotg, chan, chnum, qtd);
- if (hcint & HCINTMSK_DATATGLERR)
+ if (!dwc2_check_qtd_still_ok(qtd, chan->qh))
+ goto exit;
+ }
+ if (hcint & HCINTMSK_DATATGLERR) {
dwc2_hc_datatglerr_intr(hsotg, chan, chnum, qtd);
+ if (!dwc2_check_qtd_still_ok(qtd, chan->qh))
+ goto exit;
+ }
+exit:
chan->hcint = 0;
}
Index: src/sys/external/bsd/dwc2/dist/dwc2_hcdqueue.c
diff -u src/sys/external/bsd/dwc2/dist/dwc2_hcdqueue.c:1.13 src/sys/external/bsd/dwc2/dist/dwc2_hcdqueue.c:1.14
--- src/sys/external/bsd/dwc2/dist/dwc2_hcdqueue.c:1.13 Mon Aug 31 06:12:55 2015
+++ src/sys/external/bsd/dwc2/dist/dwc2_hcdqueue.c Sun Feb 14 10:53:30 2016
@@ -1,4 +1,4 @@
-/* $NetBSD: dwc2_hcdqueue.c,v 1.13 2015/08/31 06:12:55 uebayasi Exp $ */
+/* $NetBSD: dwc2_hcdqueue.c,v 1.14 2016/02/14 10:53:30 skrll Exp $ */
/*
* hcd_queue.c - DesignWare HS OTG Controller host queuing routines
@@ -42,7 +42,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: dwc2_hcdqueue.c,v 1.13 2015/08/31 06:12:55 uebayasi Exp $");
+__KERNEL_RCSID(0, "$NetBSD: dwc2_hcdqueue.c,v 1.14 2016/02/14 10:53:30 skrll Exp $");
#include <sys/types.h>
#include <sys/kmem.h>
@@ -118,6 +118,9 @@ static void dwc2_qh_init(struct dwc2_hso
USB_SPEED_HIGH : dev_speed, qh->ep_is_in,
qh->ep_type == USB_ENDPOINT_XFER_ISOC,
bytecount);
+
+ /* Ensure frame_number corresponds to the reality */
+ hsotg->frame_number = dwc2_hcd_get_frame_number(hsotg);
/* Start in a slightly future (micro)frame */
qh->sched_frame = dwc2_frame_num_inc(hsotg->frame_number,
SCHEDULE_SLOP);
@@ -247,8 +250,7 @@ struct dwc2_qh *dwc2_hcd_qh_create(struc
void dwc2_hcd_qh_free(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
{
struct dwc2_softc *sc = hsotg->hsotg_sc;
-
- if (hsotg->core_params->dma_desc_enable > 0) {
+ if (qh->desc_list) {
dwc2_hcd_qh_free_ddma(hsotg, qh);
} else if (qh->dw_align_buf) {
usb_freemem(&sc->sc_bus, &qh->dw_align_buf_usbdma);
@@ -602,6 +604,14 @@ int dwc2_hcd_qh_add(struct dwc2_hsotg *h
/* QH already in a schedule */
return 0;
+ if (!dwc2_frame_num_le(qh->sched_frame, hsotg->frame_number) &&
+ !hsotg->frame_number) {
+ dev_dbg(hsotg->dev,
+ "reset frame number counter\n");
+ qh->sched_frame = dwc2_frame_num_inc(hsotg->frame_number,
+ SCHEDULE_SLOP);
+ }
+
/* Add the new QH to the appropriate schedule */
if (dwc2_qh_is_non_per(qh)) {
/* Always start in inactive schedule */
Index: src/sys/external/bsd/dwc2/dist/dwc2_hw.h
diff -u src/sys/external/bsd/dwc2/dist/dwc2_hw.h:1.3 src/sys/external/bsd/dwc2/dist/dwc2_hw.h:1.4
--- src/sys/external/bsd/dwc2/dist/dwc2_hw.h:1.3 Sun Aug 30 12:59:59 2015
+++ src/sys/external/bsd/dwc2/dist/dwc2_hw.h Sun Feb 14 10:53:30 2016
@@ -1,4 +1,4 @@
-/* $NetBSD: dwc2_hw.h,v 1.3 2015/08/30 12:59:59 skrll Exp $ */
+/* $NetBSD: dwc2_hw.h,v 1.4 2016/02/14 10:53:30 skrll Exp $ */
/*
* hw.h - DesignWare HS OTG Controller hardware definitions
@@ -144,6 +144,7 @@
#define GINTSTS_RESETDET (1 << 23)
#define GINTSTS_FET_SUSP (1 << 22)
#define GINTSTS_INCOMPL_IP (1 << 21)
+#define GINTSTS_INCOMPL_SOOUT (1 << 21)
#define GINTSTS_INCOMPL_SOIN (1 << 20)
#define GINTSTS_OEPINT (1 << 19)
#define GINTSTS_IEPINT (1 << 18)
@@ -770,10 +771,6 @@
#define TSIZ_XFERSIZE_SHIFT 0
#define HCDMA(_ch) HSOTG_REG(0x0514 + 0x20 * (_ch))
-#define HCDMA_DMA_ADDR_MASK (0x1fffff << 11)
-#define HCDMA_DMA_ADDR_SHIFT 11
-#define HCDMA_CTD_MASK (0xff << 3)
-#define HCDMA_CTD_SHIFT 3
#define HCDMAB(_ch) HSOTG_REG(0x051c + 0x20 * (_ch))