Merge conflicts

This commit is contained in:
skrll 2015-08-30 12:59:59 +00:00
parent a0d8cb2cf5
commit 9c7e1469fe
8 changed files with 1189 additions and 228 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: dwc2_core.c,v 1.7 2015/05/01 06:58:40 hikaru Exp $ */
/* $NetBSD: dwc2_core.c,v 1.8 2015/08/30 12:59:59 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.7 2015/05/01 06:58:40 hikaru Exp $");
__KERNEL_RCSID(0, "$NetBSD: dwc2_core.c,v 1.8 2015/08/30 12:59:59 skrll Exp $");
#include <sys/types.h>
#include <sys/bus.h>
@ -67,6 +67,364 @@ __KERNEL_RCSID(0, "$NetBSD: dwc2_core.c,v 1.7 2015/05/01 06:58:40 hikaru Exp $")
#include "dwc2_core.h"
#include "dwc2_hcd.h"
#if IS_ENABLED(CONFIG_USB_DWC2_HOST) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
/**
* dwc2_backup_host_registers() - Backup controller host registers.
* When suspending usb bus, registers needs to be backuped
* if controller power is disabled once suspended.
*
* @hsotg: Programming view of the DWC_otg controller
*/
static int dwc2_backup_host_registers(struct dwc2_hsotg *hsotg)
{
struct dwc2_hregs_backup *hr;
int i;
dev_dbg(hsotg->dev, "%s\n", __func__);
/* Backup Host regs */
hr = &hsotg->hr_backup;
hr->hcfg = DWC2_READ_4(hsotg, HCFG);
hr->haintmsk = DWC2_READ_4(hsotg, HAINTMSK);
for (i = 0; i < hsotg->core_params->host_channels; ++i)
hr->hcintmsk[i] = DWC2_READ_4(hsotg, HCINTMSK(i));
hr->hprt0 = DWC2_READ_4(hsotg, HPRT0);
hr->hfir = DWC2_READ_4(hsotg, HFIR);
hr->valid = true;
return 0;
}
/**
* dwc2_restore_host_registers() - Restore controller host registers.
* When resuming usb bus, device registers needs to be restored
* if controller power were disabled.
*
* @hsotg: Programming view of the DWC_otg controller
*/
static int dwc2_restore_host_registers(struct dwc2_hsotg *hsotg)
{
struct dwc2_hregs_backup *hr;
int i;
dev_dbg(hsotg->dev, "%s\n", __func__);
/* Restore host regs */
hr = &hsotg->hr_backup;
if (!hr->valid) {
dev_err(hsotg->dev, "%s: no host registers to restore\n",
__func__);
return -EINVAL;
}
hr->valid = false;
DWC2_WRITE_4(hsotg, HCFG, hr->hcfg);
DWC2_WRITE_4(hsotg, HAINTMSK, hr->haintmsk);
for (i = 0; i < hsotg->core_params->host_channels; ++i)
DWC2_WRITE_4(hsotg, HCINTMSK(i), hr->hcintmsk[i]);
DWC2_WRITE_4(hsotg, HPRT0, hr->hprt0);
DWC2_WRITE_4(hsotg, HFIR, hr->hfir);
return 0;
}
#else
static inline int dwc2_backup_host_registers(struct dwc2_hsotg *hsotg)
{ return 0; }
static inline int dwc2_restore_host_registers(struct dwc2_hsotg *hsotg)
{ return 0; }
#endif
#if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || \
IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
/**
* dwc2_backup_device_registers() - Backup controller device registers.
* When suspending usb bus, registers needs to be backuped
* if controller power is disabled once suspended.
*
* @hsotg: Programming view of the DWC_otg controller
*/
static int dwc2_backup_device_registers(struct dwc2_hsotg *hsotg)
{
struct dwc2_dregs_backup *dr;
int i;
dev_dbg(hsotg->dev, "%s\n", __func__);
/* Backup dev regs */
dr = &hsotg->dr_backup;
dr->dcfg = DWC2_READ_4(hsotg, DCFG);
dr->dctl = DWC2_READ_4(hsotg, DCTL);
dr->daintmsk = DWC2_READ_4(hsotg, DAINTMSK);
dr->diepmsk = DWC2_READ_4(hsotg, DIEPMSK);
dr->doepmsk = DWC2_READ_4(hsotg, DOEPMSK);
for (i = 0; i < hsotg->num_of_eps; i++) {
/* Backup IN EPs */
dr->diepctl[i] = DWC2_READ_4(hsotg, DIEPCTL(i));
/* Ensure DATA PID is correctly configured */
if (dr->diepctl[i] & DXEPCTL_DPID)
dr->diepctl[i] |= DXEPCTL_SETD1PID;
else
dr->diepctl[i] |= DXEPCTL_SETD0PID;
dr->dieptsiz[i] = DWC2_READ_4(hsotg, DIEPTSIZ(i));
dr->diepdma[i] = DWC2_READ_4(hsotg, DIEPDMA(i));
/* Backup OUT EPs */
dr->doepctl[i] = DWC2_READ_4(hsotg, DOEPCTL(i));
/* Ensure DATA PID is correctly configured */
if (dr->doepctl[i] & DXEPCTL_DPID)
dr->doepctl[i] |= DXEPCTL_SETD1PID;
else
dr->doepctl[i] |= DXEPCTL_SETD0PID;
dr->doeptsiz[i] = DWC2_READ_4(hsotg, DOEPTSIZ(i));
dr->doepdma[i] = DWC2_READ_4(hsotg, DOEPDMA(i));
}
dr->valid = true;
return 0;
}
/**
* dwc2_restore_device_registers() - Restore controller device registers.
* When resuming usb bus, device registers needs to be restored
* if controller power were disabled.
*
* @hsotg: Programming view of the DWC_otg controller
*/
static int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg)
{
struct dwc2_dregs_backup *dr;
u32 dctl;
int i;
dev_dbg(hsotg->dev, "%s\n", __func__);
/* Restore dev regs */
dr = &hsotg->dr_backup;
if (!dr->valid) {
dev_err(hsotg->dev, "%s: no device registers to restore\n",
__func__);
return -EINVAL;
}
dr->valid = false;
DWC2_WRITE_4(hsotg, DCFG, dr->dcfg);
DWC2_WRITE_4(hsotg, DCTL, dr->dctl);
DWC2_WRITE_4(hsotg, DAINTMSK, dr->daintmsk);
DWC2_WRITE_4(hsotg, DIEPMSK, dr->diepmsk);
DWC2_WRITE_4(hsotg, DOEPMSK, dr->doepmsk);
for (i = 0; i < hsotg->num_of_eps; i++) {
/* Restore IN EPs */
DWC2_WRITE_4(hsotg, DIEPCTL(i), dr->diepctl[i]);
DWC2_WRITE_4(hsotg, DIEPTSIZ(i), dr->dieptsiz[i]);
DWC2_WRITE_4(hsotg, DIEPDMA(i), dr->diepdma[i]);
/* Restore OUT EPs */
DWC2_WRITE_4(hsotg, DOEPCTL(i), dr->doepctl[i]);
DWC2_WRITE_4(hsotg, DOEPTSIZ(i), dr->doeptsiz[i]);
DWC2_WRITE_4(hsotg, DOEPDMA(i), dr->doepdma[i]);
}
/* Set the Power-On Programming done bit */
dctl = DWC2_READ_4(hsotg, DCTL);
dctl |= DCTL_PWRONPRGDONE;
DWC2_WRITE_4(hsotg, DCTL, dctl);
return 0;
}
#else
static inline int dwc2_backup_device_registers(struct dwc2_hsotg *hsotg)
{ return 0; }
static inline int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg)
{ return 0; }
#endif
/**
* dwc2_backup_global_registers() - Backup global controller registers.
* When suspending usb bus, registers needs to be backuped
* if controller power is disabled once suspended.
*
* @hsotg: Programming view of the DWC_otg controller
*/
static int dwc2_backup_global_registers(struct dwc2_hsotg *hsotg)
{
struct dwc2_gregs_backup *gr;
int i;
/* Backup global regs */
gr = &hsotg->gr_backup;
gr->gotgctl = DWC2_READ_4(hsotg, GOTGCTL);
gr->gintmsk = DWC2_READ_4(hsotg, GINTMSK);
gr->gahbcfg = DWC2_READ_4(hsotg, GAHBCFG);
gr->gusbcfg = DWC2_READ_4(hsotg, GUSBCFG);
gr->grxfsiz = DWC2_READ_4(hsotg, GRXFSIZ);
gr->gnptxfsiz = DWC2_READ_4(hsotg, GNPTXFSIZ);
gr->hptxfsiz = DWC2_READ_4(hsotg, HPTXFSIZ);
gr->gdfifocfg = DWC2_READ_4(hsotg, GDFIFOCFG);
for (i = 0; i < MAX_EPS_CHANNELS; i++)
gr->dtxfsiz[i] = DWC2_READ_4(hsotg, DPTXFSIZN(i));
gr->valid = true;
return 0;
}
/**
* dwc2_restore_global_registers() - Restore controller global registers.
* When resuming usb bus, device registers needs to be restored
* if controller power were disabled.
*
* @hsotg: Programming view of the DWC_otg controller
*/
static int dwc2_restore_global_registers(struct dwc2_hsotg *hsotg)
{
struct dwc2_gregs_backup *gr;
int i;
dev_dbg(hsotg->dev, "%s\n", __func__);
/* Restore global regs */
gr = &hsotg->gr_backup;
if (!gr->valid) {
dev_err(hsotg->dev, "%s: no global registers to restore\n",
__func__);
return -EINVAL;
}
gr->valid = false;
DWC2_WRITE_4(hsotg, GINTSTS, 0xffffffff);
DWC2_WRITE_4(hsotg, GOTGCTL, gr->gotgctl);
DWC2_WRITE_4(hsotg, GINTMSK, gr->gintmsk);
DWC2_WRITE_4(hsotg, GUSBCFG, gr->gusbcfg);
DWC2_WRITE_4(hsotg, GAHBCFG, gr->gahbcfg);
DWC2_WRITE_4(hsotg, GRXFSIZ, gr->grxfsiz);
DWC2_WRITE_4(hsotg, GNPTXFSIZ, gr->gnptxfsiz);
DWC2_WRITE_4(hsotg, HPTXFSIZ, gr->hptxfsiz);
DWC2_WRITE_4(hsotg, GDFIFOCFG, gr->gdfifocfg);
for (i = 0; i < MAX_EPS_CHANNELS; i++)
DWC2_WRITE_4(hsotg, DPTXFSIZN(i), gr->dtxfsiz[i]);
return 0;
}
/**
* dwc2_exit_hibernation() - Exit controller from Partial Power Down.
*
* @hsotg: Programming view of the DWC_otg controller
* @restore: Controller registers need to be restored
*/
int dwc2_exit_hibernation(struct dwc2_hsotg *hsotg, bool restore)
{
u32 pcgcctl;
int ret = 0;
if (!hsotg->core_params->hibernation)
return -ENOTSUPP;
pcgcctl = DWC2_READ_4(hsotg, PCGCTL);
pcgcctl &= ~PCGCTL_STOPPCLK;
DWC2_WRITE_4(hsotg, PCGCTL, pcgcctl);
pcgcctl = DWC2_READ_4(hsotg, PCGCTL);
pcgcctl &= ~PCGCTL_PWRCLMP;
DWC2_WRITE_4(hsotg, PCGCTL, pcgcctl);
pcgcctl = DWC2_READ_4(hsotg, PCGCTL);
pcgcctl &= ~PCGCTL_RSTPDWNMODULE;
DWC2_WRITE_4(hsotg, PCGCTL, pcgcctl);
udelay(100);
if (restore) {
ret = dwc2_restore_global_registers(hsotg);
if (ret) {
dev_err(hsotg->dev, "%s: failed to restore registers\n",
__func__);
return ret;
}
if (dwc2_is_host_mode(hsotg)) {
ret = dwc2_restore_host_registers(hsotg);
if (ret) {
dev_err(hsotg->dev, "%s: failed to restore host registers\n",
__func__);
return ret;
}
} else {
ret = dwc2_restore_device_registers(hsotg);
if (ret) {
dev_err(hsotg->dev, "%s: failed to restore device registers\n",
__func__);
return ret;
}
}
}
return ret;
}
/**
* dwc2_enter_hibernation() - Put controller in Partial Power Down.
*
* @hsotg: Programming view of the DWC_otg controller
*/
int dwc2_enter_hibernation(struct dwc2_hsotg *hsotg)
{
u32 pcgcctl;
int ret = 0;
if (!hsotg->core_params->hibernation)
return -ENOTSUPP;
/* Backup all registers */
ret = dwc2_backup_global_registers(hsotg);
if (ret) {
dev_err(hsotg->dev, "%s: failed to backup global registers\n",
__func__);
return ret;
}
if (dwc2_is_host_mode(hsotg)) {
ret = dwc2_backup_host_registers(hsotg);
if (ret) {
dev_err(hsotg->dev, "%s: failed to backup host registers\n",
__func__);
return ret;
}
} else {
ret = dwc2_backup_device_registers(hsotg);
if (ret) {
dev_err(hsotg->dev, "%s: failed to backup device registers\n",
__func__);
return ret;
}
}
/* Put the controller in low power state */
pcgcctl = DWC2_READ_4(hsotg, PCGCTL);
pcgcctl |= PCGCTL_PWRCLMP;
DWC2_WRITE_4(hsotg, PCGCTL, pcgcctl);
ndelay(20);
pcgcctl |= PCGCTL_RSTPDWNMODULE;
DWC2_WRITE_4(hsotg, PCGCTL, pcgcctl);
ndelay(20);
pcgcctl |= PCGCTL_STOPPCLK;
DWC2_WRITE_4(hsotg, PCGCTL, pcgcctl);
return ret;
}
/**
* dwc2_enable_common_interrupts() - Initializes the commmon interrupts,
* used in both device and host modes
@ -88,8 +446,10 @@ static void dwc2_enable_common_interrupts(struct dwc2_hsotg *hsotg)
if (hsotg->core_params->dma_enable <= 0)
intmsk |= GINTSTS_RXFLVL;
if (hsotg->core_params->external_id_pin_ctl <= 0)
intmsk |= GINTSTS_CONIDSTSCHNG;
intmsk |= GINTSTS_CONIDSTSCHNG | GINTSTS_WKUPINT | GINTSTS_USBSUSP |
intmsk |= GINTSTS_WKUPINT | GINTSTS_USBSUSP |
GINTSTS_SESSREQINT;
DWC2_WRITE_4(hsotg, GINTMSK, intmsk);
@ -129,6 +489,7 @@ static int dwc2_core_reset(struct dwc2_hsotg *hsotg)
{
u32 greset;
int count = 0;
u32 gusbcfg;
dev_vdbg(hsotg->dev, "%s()\n", __func__);
@ -159,6 +520,23 @@ static int dwc2_core_reset(struct dwc2_hsotg *hsotg)
}
} while (greset & GRSTCTL_CSFTRST);
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);
}
/*
* NOTE: This long sleep is _very_ important, otherwise the core will
* not stay in host mode after a connector ID change!
@ -312,13 +690,8 @@ static int dwc2_gahbcfg_init(struct dwc2_hsotg *hsotg)
switch (hsotg->hw_params.arch) {
case GHWCFG2_EXT_DMA_ARCH:
dev_dbg(hsotg->dev, "External DMA Mode\n");
if (hsotg->core_params->ahbcfg != -1) {
ahbcfg &= GAHBCFG_CTRL_MASK;
ahbcfg |= hsotg->core_params->ahbcfg &
~GAHBCFG_CTRL_MASK;
}
break;
dev_err(hsotg->dev, "External DMA Mode not supported\n");
return -EINVAL;
case GHWCFG2_INT_DMA_ARCH:
dev_dbg(hsotg->dev, "Internal DMA Mode\n");
@ -460,7 +833,7 @@ int dwc2_core_init(struct dwc2_hsotg *hsotg, bool select_phy)
dwc2_enable_common_interrupts(hsotg);
/*
* Do device or host intialization based on mode during PCD and
* Do device or host initialization based on mode during PCD and
* HCD initialization
*/
if (dwc2_is_host_mode(hsotg)) {
@ -513,6 +886,72 @@ void dwc2_disable_host_interrupts(struct dwc2_hsotg *hsotg)
DWC2_WRITE_4(hsotg, GINTMSK, intmsk);
}
/*
* dwc2_calculate_dynamic_fifo() - Calculates the default fifo size
* For system that have a total fifo depth that is smaller than the default
* RX + TX fifo size.
*
* @hsotg: Programming view of DWC_otg controller
*/
static void dwc2_calculate_dynamic_fifo(struct dwc2_hsotg *hsotg)
{
struct dwc2_core_params *params = hsotg->core_params;
struct dwc2_hw_params *hw = &hsotg->hw_params;
u32 rxfsiz, nptxfsiz, ptxfsiz, total_fifo_size;
total_fifo_size = hw->total_fifo_size;
rxfsiz = params->host_rx_fifo_size;
nptxfsiz = params->host_nperio_tx_fifo_size;
ptxfsiz = params->host_perio_tx_fifo_size;
/*
* Will use Method 2 defined in the DWC2 spec: minimum FIFO depth
* allocation with support for high bandwidth endpoints. Synopsys
* defines MPS(Max Packet size) for a periodic EP=1024, and for
* non-periodic as 512.
*/
if (total_fifo_size < (rxfsiz + nptxfsiz + ptxfsiz)) {
/*
* For Buffer DMA mode/Scatter Gather DMA mode
* 2 * ((Largest Packet size / 4) + 1 + 1) + n
* with n = number of host channel.
* 2 * ((1024/4) + 2) = 516
*/
rxfsiz = 516 + hw->host_channels;
/*
* min non-periodic tx fifo depth
* 2 * (largest non-periodic USB packet used / 4)
* 2 * (512/4) = 256
*/
nptxfsiz = 256;
/*
* min periodic tx fifo depth
* (largest packet size*MC)/4
* (1024 * 3)/4 = 768
*/
ptxfsiz = 768;
params->host_rx_fifo_size = rxfsiz;
params->host_nperio_tx_fifo_size = nptxfsiz;
params->host_perio_tx_fifo_size = ptxfsiz;
}
/*
* If the summation of RX, NPTX and PTX fifo sizes is still
* bigger than the total_fifo_size, then we have a problem.
*
* We won't be able to allocate as many endpoints. Right now,
* we're just printing an error message, but ideally this FIFO
* allocation algorithm would be improved in the future.
*
* FIXME improve this FIFO allocation algorithm.
*/
if (unlikely(total_fifo_size < (rxfsiz + nptxfsiz + ptxfsiz)))
dev_err(hsotg->dev, "invalid fifo sizes\n");
}
static void dwc2_config_fifos(struct dwc2_hsotg *hsotg)
{
struct dwc2_core_params *params = hsotg->core_params;
@ -521,6 +960,8 @@ static void dwc2_config_fifos(struct dwc2_hsotg *hsotg)
if (!params->enable_dynamic_fifo)
return;
dwc2_calculate_dynamic_fifo(hsotg);
/* Rx FIFO */
grxfsiz = DWC2_READ_4(hsotg, GRXFSIZ);
dev_dbg(hsotg->dev, "initial grxfsiz=%08x\n", grxfsiz);
@ -1401,18 +1842,10 @@ void dwc2_hc_start_transfer(struct dwc2_hsotg *hsotg,
} else {
dma_addr = chan->xfer_dma;
}
if (hsotg->hsotg_sc->sc_set_dma_addr == NULL) {
DWC2_WRITE_4(hsotg, HCDMA(chan->hc_num),
(u32)dma_addr);
if (dbg_hc(chan))
dev_vdbg(hsotg->dev,
"Wrote %08lx to HCDMA(%d)\n",
(unsigned long)dma_addr,
chan->hc_num);
} else {
(void)(*hsotg->hsotg_sc->sc_set_dma_addr)(
hsotg->dev, dma_addr, chan->hc_num);
}
DWC2_WRITE_4(hsotg, HCDMA(chan->hc_num), (u32)dma_addr);
if (dbg_hc(chan))
dev_vdbg(hsotg->dev, "Wrote %08lx to HCDMA(%d)\n",
(unsigned long)dma_addr, chan->hc_num);
}
/* Start the split */
@ -2539,6 +2972,40 @@ static void dwc2_set_param_uframe_sched(struct dwc2_hsotg *hsotg, int val)
hsotg->core_params->uframe_sched = val;
}
static void dwc2_set_param_external_id_pin_ctl(struct dwc2_hsotg *hsotg,
int val)
{
if (DWC2_OUT_OF_BOUNDS(val, 0, 1)) {
if (val >= 0) {
dev_err(hsotg->dev,
"'%d' invalid for parameter external_id_pin_ctl\n",
val);
dev_err(hsotg->dev, "external_id_pin_ctl must be 0 or 1\n");
}
val = 0;
dev_dbg(hsotg->dev, "Setting external_id_pin_ctl to %d\n", val);
}
hsotg->core_params->external_id_pin_ctl = val;
}
static void dwc2_set_param_hibernation(struct dwc2_hsotg *hsotg,
int val)
{
if (DWC2_OUT_OF_BOUNDS(val, 0, 1)) {
if (val >= 0) {
dev_err(hsotg->dev,
"'%d' invalid for parameter hibernation\n",
val);
dev_err(hsotg->dev, "hibernation must be 0 or 1\n");
}
val = 0;
dev_dbg(hsotg->dev, "Setting hibernation to %d\n", val);
}
hsotg->core_params->hibernation = val;
}
/*
* This function is called during module intialization to pass module parameters
* for the DWC_otg core.
@ -2583,6 +3050,8 @@ void dwc2_set_parameters(struct dwc2_hsotg *hsotg,
dwc2_set_param_ahbcfg(hsotg, params->ahbcfg);
dwc2_set_param_otg_ver(hsotg, params->otg_ver);
dwc2_set_param_uframe_sched(hsotg, params->uframe_sched);
dwc2_set_param_external_id_pin_ctl(hsotg, params->external_id_pin_ctl);
dwc2_set_param_hibernation(hsotg, params->hibernation);
}
/**
@ -2618,23 +3087,23 @@ int dwc2_get_hwparams(struct dwc2_hsotg *hsotg)
hwcfg2 = DWC2_READ_4(hsotg, GHWCFG2);
hwcfg3 = DWC2_READ_4(hsotg, GHWCFG3);
hwcfg4 = DWC2_READ_4(hsotg, GHWCFG4);
gnptxfsiz = DWC2_READ_4(hsotg, GNPTXFSIZ);
grxfsiz = DWC2_READ_4(hsotg, GRXFSIZ);
dev_dbg(hsotg->dev, "hwcfg1=%08x\n", DWC2_READ_4(hsotg, GHWCFG1));
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, "gnptxfsiz=%08x\n", gnptxfsiz);
dev_dbg(hsotg->dev, "grxfsiz=%08x\n", grxfsiz);
/* Force host mode to get HPTXFSIZ exact power on value */
/* 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);
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;
@ -2669,6 +3138,13 @@ int dwc2_get_hwparams(struct dwc2_hsotg *hsotg)
width = (hwcfg3 & GHWCFG3_XFER_SIZE_CNTR_WIDTH_MASK) >>
GHWCFG3_XFER_SIZE_CNTR_WIDTH_SHIFT;
hw->max_transfer_size = (1 << (width + 11)) - 1;
/*
* Clip max_transfer_size to 65535. dwc2_hc_setup_align_buf() allocates
* coherent buffers with this size, and if it's too large we can
* exhaust the coherent DMA pool.
*/
if (hw->max_transfer_size > 65535)
hw->max_transfer_size = 65535;
width = (hwcfg3 & GHWCFG3_PACKET_SIZE_CNTR_WIDTH_MASK) >>
GHWCFG3_PACKET_SIZE_CNTR_WIDTH_SHIFT;
hw->max_packet_count = (1 << (width + 4)) - 1;
@ -2743,6 +3219,22 @@ int dwc2_get_hwparams(struct dwc2_hsotg *hsotg)
return 0;
}
/*
* Sets all parameters to the given value.
*
* Assumes that the dwc2_core_params struct contains only integers.
*/
void dwc2_set_all_params(struct dwc2_core_params *params, int value)
{
int *p = (int *)params;
size_t size = sizeof(*params) / sizeof(*p);
int i;
for (i = 0; i < size; i++)
p[i] = value;
}
u16 dwc2_get_otg_version(struct dwc2_hsotg *hsotg)
{
return hsotg->core_params->otg_ver == 1 ? 0x0200 : 0x0103;

View File

@ -1,4 +1,4 @@
/* $NetBSD: dwc2_core.h,v 1.5 2014/04/03 06:34:58 skrll Exp $ */
/* $NetBSD: dwc2_core.h,v 1.6 2015/08/30 12:59:59 skrll Exp $ */
/*
* core.h - DesignWare HS OTG Controller common declarations
@ -53,6 +53,129 @@
/* Maximum number of Endpoints/HostChannels */
#define MAX_EPS_CHANNELS 16
#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[] = {
"vusb_d", /* digital USB supply, 1.2V */
"vusb_a", /* analog USB supply, 1.1V */
};
/*
* EP0_MPS_LIMIT
*
* Unfortunately there seems to be a limit of the amount of data that can
* be transferred by IN transactions on EP0. This is either 127 bytes or 3
* packets (which practically means 1 packet and 63 bytes of data) when the
* MPS is set to 64.
*
* This means if we are wanting to move >127 bytes of data, we need to
* split the transactions up, but just doing one packet at a time does
* not work (this may be an implicit DATA0 PID on first packet of the
* transaction) and doing 2 packets is outside the controller's limits.
*
* If we try to lower the MPS size for EP0, then no transfers work properly
* for EP0, and the system will fail basic enumeration. As no cause for this
* has currently been found, we cannot support any large IN transfers for
* EP0.
*/
#define EP0_MPS_LIMIT 64
struct dwc2_hsotg;
struct s3c_hsotg_req;
/**
* struct s3c_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.
* @parent: Reference back to the parent device structure.
* @req: The current request that the endpoint is processing. This is
* used to indicate an request has been loaded onto the endpoint
* and has yet to be completed (maybe due to data move, or simply
* awaiting an ack from the core all the data has been completed).
* @debugfs: File entry for debugfs file for this endpoint.
* @lock: State lock to protect contents of endpoint.
* @dir_in: Set to true if this endpoint is of the IN direction, which
* means that it is sending data to the Host.
* @index: The index for the endpoint registers.
* @mc: Multi Count - number of transactions per microframe
* @interval - Interval for periodic endpoints
* @name: The name array passed to the USB core.
* @halted: Set if the endpoint has been halted.
* @periodic: Set if this is a periodic ep, such as Interrupt
* @isochronous: Set if this is a isochronous ep
* @send_zlp: Set if we need to send a zero-length packet.
* @total_data: The total number of data bytes done.
* @fifo_size: The size of the FIFO (for periodic IN endpoints)
* @fifo_load: The amount of data loaded into the FIFO (periodic IN)
* @last_load: The offset of data for the last start of request.
* @size_loaded: The last loaded size for DxEPTSIZE for periodic IN
*
* This is the driver's state for each registered enpoint, allowing it
* to keep track of transactions that need doing. Each endpoint has a
* lock to protect the state, to try and avoid using an overall lock
* for the host controller as much as possible.
*
* For periodic IN endpoints, we have fifo_size and fifo_load to try
* and keep track of the amount of data in the periodic FIFO for each
* of these as we don't have a status register that tells us how much
* is in each of them. (note, this may actually be useless information
* as in shared-fifo mode periodic in acts like a single-frame packet
* buffer than a fifo)
*/
struct s3c_hsotg_ep {
struct usb_ep ep;
struct list_head queue;
struct dwc2_hsotg *parent;
struct s3c_hsotg_req *req;
struct dentry *debugfs;
unsigned long total_data;
unsigned int size_loaded;
unsigned int last_load;
unsigned int fifo_load;
unsigned short fifo_size;
unsigned short fifo_index;
unsigned char dir_in;
unsigned char index;
unsigned char mc;
unsigned char interval;
unsigned int halted:1;
unsigned int periodic:1;
unsigned int isochronous:1;
unsigned int send_zlp:1;
char name[10];
};
/**
* struct s3c_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 usb_request req;
struct list_head queue;
void *saved_req_buf;
};
#define call_gadget(_hs, _entry) \
do { \
if ((_hs)->gadget.speed != USB_SPEED_UNKNOWN && \
(_hs)->driver && (_hs)->driver->_entry) { \
spin_unlock(&_hs->lock); \
(_hs)->driver->_entry(&(_hs)->gadget); \
spin_lock(&_hs->lock); \
} \
} while (0)
#else
#define call_gadget(_hs, _entry) do {} while (0)
#endif
struct dwc2_hsotg;
struct dwc2_host_chan;
@ -64,6 +187,22 @@ enum dwc2_lx_state {
DWC2_L3, /* Off state */
};
/*
* Gadget periodic tx fifo sizes as used by legacy driver
* EP0 is not included
*/
#define DWC2_G_P_LEGACY_TX_FIFO_SIZE {256, 256, 256, 256, 768, 768, 768, \
768, 0, 0, 0, 0, 0, 0, 0}
/* Gadget ep0 states */
enum dwc2_ep0_state {
DWC2_EP0_SETUP,
DWC2_EP0_DATA_IN,
DWC2_EP0_DATA_OUT,
DWC2_EP0_STATUS_IN,
DWC2_EP0_STATUS_OUT,
};
/**
* struct dwc2_core_params - Parameters for configuring the core
*
@ -188,6 +327,17 @@ enum dwc2_lx_state {
* by the driver and are ignored in this
* configuration value.
* @uframe_sched: True to enable the microframe scheduler
* @external_id_pin_ctl: Specifies whether ID pin is handled externally.
* Disable CONIDSTSCHNG controller interrupt in such
* case.
* 0 - No (default)
* 1 - Yes
* @hibernation: Specifies whether the controller support hibernation.
* If hibernation is enabled, the controller will enter
* hibernation in both peripheral and host mode when
* needed.
* 0 - No (default)
* 1 - Yes
*
* The following parameters may be specified when starting the module. These
* parameters define how the DWC_otg controller should be configured. A
@ -225,6 +375,8 @@ struct dwc2_core_params {
int reload_ctl;
int ahbcfg;
int uframe_sched;
int external_id_pin_ctl;
int hibernation;
};
/**
@ -252,7 +404,7 @@ struct dwc2_core_params {
* @power_optimized Are power optimizations enabled?
* @num_dev_ep Number of device endpoints available
* @num_dev_perio_in_ep Number of device periodic IN endpoints
* avaialable
* available
* @dev_token_q_depth Device Mode IN Token Sequence Learning Queue
* Depth
* 0 to 30
@ -305,19 +457,109 @@ struct dwc2_hw_params {
u32 snpsid;
};
/* Size of control and EP0 buffers */
#define DWC2_CTRL_BUFF_SIZE 8
/**
* struct dwc2_gregs_backup - Holds global registers state before entering partial
* power down
* @gotgctl: Backup of GOTGCTL register
* @gintmsk: Backup of GINTMSK register
* @gahbcfg: Backup of GAHBCFG register
* @gusbcfg: Backup of GUSBCFG register
* @grxfsiz: Backup of GRXFSIZ register
* @gnptxfsiz: Backup of GNPTXFSIZ register
* @gi2cctl: Backup of GI2CCTL register
* @hptxfsiz: Backup of HPTXFSIZ register
* @gdfifocfg: Backup of GDFIFOCFG register
* @dtxfsiz: Backup of DTXFSIZ registers for each endpoint
* @gpwrdn: Backup of GPWRDN register
*/
struct dwc2_gregs_backup {
u32 gotgctl;
u32 gintmsk;
u32 gahbcfg;
u32 gusbcfg;
u32 grxfsiz;
u32 gnptxfsiz;
u32 gi2cctl;
u32 hptxfsiz;
u32 pcgcctl;
u32 gdfifocfg;
u32 dtxfsiz[MAX_EPS_CHANNELS];
u32 gpwrdn;
bool valid;
};
/**
* struct dwc2_dregs_backup - Holds device registers state before entering partial
* power down
* @dcfg: Backup of DCFG register
* @dctl: Backup of DCTL register
* @daintmsk: Backup of DAINTMSK register
* @diepmsk: Backup of DIEPMSK register
* @doepmsk: Backup of DOEPMSK register
* @diepctl: Backup of DIEPCTL register
* @dieptsiz: Backup of DIEPTSIZ register
* @diepdma: Backup of DIEPDMA register
* @doepctl: Backup of DOEPCTL register
* @doeptsiz: Backup of DOEPTSIZ register
* @doepdma: Backup of DOEPDMA register
*/
struct dwc2_dregs_backup {
u32 dcfg;
u32 dctl;
u32 daintmsk;
u32 diepmsk;
u32 doepmsk;
u32 diepctl[MAX_EPS_CHANNELS];
u32 dieptsiz[MAX_EPS_CHANNELS];
u32 diepdma[MAX_EPS_CHANNELS];
u32 doepctl[MAX_EPS_CHANNELS];
u32 doeptsiz[MAX_EPS_CHANNELS];
u32 doepdma[MAX_EPS_CHANNELS];
bool valid;
};
/**
* struct dwc2_hregs_backup - Holds host registers state before entering partial
* power down
* @hcfg: Backup of HCFG register
* @haintmsk: Backup of HAINTMSK register
* @hcintmsk: Backup of HCINTMSK register
* @hptr0: Backup of HPTR0 register
* @hfir: Backup of HFIR register
*/
struct dwc2_hregs_backup {
u32 hcfg;
u32 haintmsk;
u32 hcintmsk[MAX_EPS_CHANNELS];
u32 hprt0;
u32 hfir;
bool valid;
};
/**
* struct dwc2_hsotg - Holds the state of the driver, including the non-periodic
* and periodic schedules
*
* These are common for both host and peripheral modes:
*
* @dev: The struct device pointer
* @regs: Pointer to controller regs
* @core_params: Parameters that define how the core should be configured
* @hw_params: Parameters that were autodetected from the
* hardware registers
* @core_params: Parameters that define how the core should be configured
* @op_state: The operational State, during transitions (a_host=>
* a_peripheral and b_device=>b_host) this may not match
* the core, but allows the software to determine
* transitions
* @dr_mode: Requested mode of operation, one of following:
* - USB_DR_MODE_PERIPHERAL
* - USB_DR_MODE_HOST
* - USB_DR_MODE_OTG
* @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
* transfer are in process of being queued
* @srp_success: Stores status of SRP request in the case of a FS PHY
@ -327,6 +569,12 @@ struct dwc2_hw_params {
* interrupt
* @wkp_timer: Timer object for handling Wakeup Detected interrupt
* @lx_state: Lx state of connected device
* @gregs_backup: Backup of global registers during suspend
* @dregs_backup: Backup of device registers during suspend
* @hregs_backup: Backup of host registers during suspend
*
* These are for host mode:
*
* @flags: Flags for handling root port state changes
* @non_periodic_sched_inactive: Inactive QHs in the non-periodic schedule.
* Transfers associated with these QHs are not currently
@ -395,11 +643,37 @@ struct dwc2_hw_params {
* @status_buf_dma: DMA address for status_buf
* @start_work: Delayed work for handling host A-cable connection
* @reset_work: Delayed work for handling a port reset
* @lock: Spinlock that protects all the driver data structures
* @priv: Stores a pointer to the struct usb_hcd
* @otg_port: OTG port number
* @frame_list: Frame list
* @frame_list_dma: Frame list DMA address
*
* 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.
* @debug_file: Main status file for debugfs.
* @debug_testmode: Testmode status file for debugfs.
* @debug_fifo: FIFO status file for debugfs.
* @ep0_reply: Request used for ep0 reply.
* @ep0_buff: Buffer for EP0 reply data, if needed.
* @ctrl_buff: Buffer for EP0 control requests.
* @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
* @g_np_g_tx_fifo_sz: Contains Non-Periodic tx fifo size value
* @g_tx_fifo_sz: Contains tx fifo size value per endpoints
*/
struct dwc2_hsotg {
device_t dev;
@ -409,6 +683,21 @@ struct dwc2_hsotg {
/** Params to actually use */
struct dwc2_core_params *core_params;
enum usb_otg_state op_state;
enum usb_dr_mode dr_mode;
unsigned int hcd_enabled:1;
unsigned int gadget_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 mutex init_mutex;
int irq;
struct clk *clk;
#endif /* CONFIG_USB_DWC2_PERIPHERAL || CONFIG_USB_DWC2_DUAL_ROLE */
unsigned int queuing_high_bandwidth:1;
unsigned int srp_success:1;
@ -417,7 +706,21 @@ struct dwc2_hsotg {
struct work wf_otg;
struct callout wkp_timer;
enum dwc2_lx_state lx_state;
struct dwc2_gregs_backup gr_backup;
struct dwc2_dregs_backup dr_backup;
struct dwc2_hregs_backup hr_backup;
struct dentry *debug_root;
struct debugfs_regset32 *regset;
/* DWC OTG HW Release versions */
#define DWC2_CORE_REV_2_71a 0x4f54271a
#define DWC2_CORE_REV_2_90a 0x4f54290a
#define DWC2_CORE_REV_2_92a 0x4f54292a
#define DWC2_CORE_REV_2_94a 0x4f54294a
#define DWC2_CORE_REV_3_00a 0x4f54300a
#if IS_ENABLED(CONFIG_USB_DWC2_HOST) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
union dwc2_hcd_internal_flags {
u32 d32;
struct {
@ -428,7 +731,7 @@ struct dwc2_hsotg {
unsigned port_suspend_change:1;
unsigned port_over_current_change:1;
unsigned port_l1_change:1;
unsigned reserved:26;
unsigned reserved:25;
} b;
} flags;
@ -465,20 +768,11 @@ struct dwc2_hsotg {
struct delayed_work start_work;
struct delayed_work reset_work;
spinlock_t lock;
void *priv;
u8 otg_port;
usb_dma_t frame_list_usbdma;
u32 *frame_list;
dma_addr_t frame_list_dma;
/* DWC OTG HW Release versions */
#define DWC2_CORE_REV_2_71a 0x4f54271a
#define DWC2_CORE_REV_2_90a 0x4f54290a
#define DWC2_CORE_REV_2_92a 0x4f54292a
#define DWC2_CORE_REV_2_94a 0x4f54294a
#define DWC2_CORE_REV_3_00a 0x4f54300a
#ifdef DEBUG
u32 frrem_samples;
u64 frrem_accum;
@ -497,6 +791,37 @@ struct dwc2_hsotg {
u32 hfnum_other_samples_b;
u64 hfnum_other_frrem_accum_b;
#endif
#endif /* CONFIG_USB_DWC2_HOST || CONFIG_USB_DWC2_DUAL_ROLE */
#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;
u32 fifo_map;
struct usb_request *ep0_reply;
struct usb_request *ctrl_req;
void *ep0_buff;
void *ctrl_buff;
enum dwc2_ep0_state ep0_state;
u8 test_mode;
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];
u32 g_using_dma;
u32 g_rx_fifo_sz;
u32 g_np_g_tx_fifo_sz;
u32 g_tx_fifo_sz[MAX_EPS_CHANNELS];
#endif /* CONFIG_USB_DWC2_PERIPHERAL || CONFIG_USB_DWC2_DUAL_ROLE */
};
/* Reasons for halting a host channel */
@ -522,6 +847,8 @@ enum dwc2_halt_status {
* and the DWC_otg controller
*/
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);
/*
* Host core Functions.
@ -754,6 +1081,15 @@ extern void dwc2_set_param_ahbcfg(struct dwc2_hsotg *hsotg, int val);
extern void dwc2_set_param_otg_ver(struct dwc2_hsotg *hsotg, int val);
extern void dwc2_set_parameters(struct dwc2_hsotg *hsotg,
const struct dwc2_core_params *params);
extern void dwc2_set_all_params(struct dwc2_core_params *params, int value);
extern int dwc2_get_hwparams(struct dwc2_hsotg *hsotg);
/*
* Dump core registers and SPRAM
*/
@ -766,4 +1102,49 @@ extern void dwc2_dump_global_registers(struct dwc2_hsotg *hsotg);
*/
extern u16 dwc2_get_otg_version(struct dwc2_hsotg *hsotg);
/* 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_gadget_init(struct dwc2_hsotg *hsotg, int irq);
extern void s3c_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);
#define dwc2_is_device_connected(hsotg) (hsotg->connected)
#else
static inline int s3c_hsotg_remove(struct dwc2_hsotg *dwc2)
{ return 0; }
static inline int s3c_hsotg_suspend(struct dwc2_hsotg *dwc2)
{ return 0; }
static inline int s3c_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,
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,
int testmode)
{ return 0; }
#define dwc2_is_device_connected(hsotg) (0)
#endif
#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_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_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)
{ return 0; }
#endif
#endif /* __DWC2_CORE_H__ */

View File

@ -1,4 +1,4 @@
/* $NetBSD: dwc2_coreintr.c,v 1.8 2014/04/04 05:40:57 skrll Exp $ */
/* $NetBSD: dwc2_coreintr.c,v 1.9 2015/08/30 12:59:59 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.8 2014/04/04 05:40:57 skrll Exp $");
__KERNEL_RCSID(0, "$NetBSD: dwc2_coreintr.c,v 1.9 2015/08/30 12:59:59 skrll Exp $");
#include <sys/param.h>
#include <sys/kernel.h>
@ -57,6 +57,7 @@ __KERNEL_RCSID(0, "$NetBSD: dwc2_coreintr.c,v 1.8 2014/04/04 05:40:57 skrll Exp
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/err.h>
#include <dwc2/dwc2.h>
#include <dwc2/dwc2var.h>
@ -141,6 +142,9 @@ static void dwc2_handle_otg_intr(struct dwc2_hsotg *hsotg)
dwc2_op_state_str(hsotg));
gotgctl = DWC2_READ_4(hsotg, GOTGCTL);
if (dwc2_is_device_mode(hsotg))
s3c_hsotg_disconnect(hsotg);
if (hsotg->op_state == OTG_STATE_B_HOST) {
hsotg->op_state = OTG_STATE_B_PERIPHERAL;
} else {
@ -300,9 +304,11 @@ static void dwc2_handle_conn_id_status_change_intr(struct dwc2_hsotg *hsotg)
* Release lock before scheduling workq as it holds spinlock during
* scheduling.
*/
spin_unlock(&hsotg->lock);
workqueue_enqueue(hsotg->wq_otg, &hsotg->wf_otg, NULL);
spin_lock(&hsotg->lock);
if (hsotg->wq_otg) {
spin_unlock(&hsotg->lock);
workqueue_enqueue(hsotg->wq_otg, &hsotg->wf_otg, NULL);
spin_lock(&hsotg->lock);
}
/* Clear interrupt */
DWC2_WRITE_4(hsotg, GINTSTS, GINTSTS_CONIDSTSCHNG);
@ -325,6 +331,12 @@ static void dwc2_handle_session_req_intr(struct dwc2_hsotg *hsotg)
/* 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);
}
/*
@ -336,6 +348,7 @@ static void dwc2_handle_session_req_intr(struct dwc2_hsotg *hsotg)
*/
static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg)
{
int ret;
dev_dbg(hsotg->dev, "++Resume or Remote Wakeup Detected Interrupt++\n");
dev_dbg(hsotg->dev, "%s lxstate = %d\n", __func__, hsotg->lx_state);
@ -347,6 +360,11 @@ static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg)
/* Clear Remote Wakeup Signaling */
dctl &= ~DCTL_RMTWKUPSIG;
DWC2_WRITE_4(hsotg, DCTL, dctl);
ret = dwc2_exit_hibernation(hsotg, true);
if (ret && (ret != -ENOTSUPP))
dev_err(hsotg->dev, "exit hibernation failed\n");
call_gadget(hsotg, resume);
}
/* Change to L0 state */
hsotg->lx_state = DWC2_L0;
@ -379,6 +397,9 @@ static void dwc2_handle_disconnect_intr(struct dwc2_hsotg *hsotg)
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;
@ -395,12 +416,12 @@ static void dwc2_handle_disconnect_intr(struct dwc2_hsotg *hsotg)
*/
static void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg)
{
u32 dsts;
int ret;
dev_dbg(hsotg->dev, "USB SUSPEND\n");
if (dwc2_is_device_mode(hsotg)) {
#ifdef DWC2_DEBUG
u32 dsts;
/*
* Check the Device status register to determine if the Suspend
* state is active
@ -411,11 +432,43 @@ static void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg)
"DSTS.Suspend Status=%d HWCFG4.Power Optimize=%d\n",
!!(dsts & DSTS_SUSPSTS),
hsotg->hw_params.power_optimized);
#endif
if ((dsts & DSTS_SUSPSTS) && hsotg->hw_params.power_optimized) {
/* Ignore suspend request before enumeration */
if (!dwc2_is_device_connected(hsotg)) {
dev_dbg(hsotg->dev,
"ignore suspend request before enumeration\n");
goto clear_int;
}
ret = dwc2_enter_hibernation(hsotg);
if (ret) {
if (ret != -ENOTSUPP)
dev_err(hsotg->dev,
"enter hibernation failed\n");
goto skip_power_saving;
}
udelay(100);
/* Ask phy to be suspended */
if (!IS_ERR_OR_NULL(hsotg->uphy))
usb_phy_set_suspend(hsotg->uphy, true);
skip_power_saving:
/*
* Change to L2 (suspend) state before releasing
* spinlock
*/
hsotg->lx_state = DWC2_L2;
/* Call gadget suspend callback */
call_gadget(hsotg, suspend);
}
} else {
if (hsotg->op_state == OTG_STATE_A_PERIPHERAL) {
dev_dbg(hsotg->dev, "a_peripheral->a_host\n");
/* Change to L2 (suspend) state */
hsotg->lx_state = DWC2_L2;
/* Clear the a_peripheral flag, back to a_host */
spin_unlock(&hsotg->lock);
dwc2_hcd_start(hsotg);
@ -424,9 +477,7 @@ static void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg)
}
}
/* Change to L2 (suspend) state */
hsotg->lx_state = DWC2_L2;
clear_int:
/* Clear interrupt */
DWC2_WRITE_4(hsotg, GINTSTS, GINTSTS_USBSUSP);
}

View File

@ -1,4 +1,4 @@
/* $NetBSD: dwc2_hcd.c,v 1.15 2014/11/24 10:14:14 skrll Exp $ */
/* $NetBSD: dwc2_hcd.c,v 1.16 2015/08/30 12:59:59 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.15 2014/11/24 10:14:14 skrll Exp $");
__KERNEL_RCSID(0, "$NetBSD: dwc2_hcd.c,v 1.16 2015/08/30 12:59:59 skrll Exp $");
#include <sys/types.h>
#include <sys/kmem.h>
@ -57,6 +57,7 @@ __KERNEL_RCSID(0, "$NetBSD: dwc2_hcd.c,v 1.15 2014/11/24 10:14:14 skrll Exp $");
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/err.h>
#include <dwc2/dwc2.h>
#include <dwc2/dwc2var.h>
@ -267,6 +268,14 @@ static void dwc2_hcd_cleanup_channels(struct dwc2_hsotg *hsotg)
*/
channel->qh = NULL;
}
/* All channels have been freed, mark them available */
if (hsotg->core_params->uframe_sched > 0) {
hsotg->available_host_channels =
hsotg->core_params->host_channels;
} else {
hsotg->non_periodic_channels = 0;
hsotg->periodic_channels = 0;
}
}
/**
@ -328,10 +337,12 @@ void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg)
*/
static void dwc2_hcd_rem_wakeup(struct dwc2_hsotg *hsotg)
{
if (hsotg->lx_state == DWC2_L2)
if (hsotg->lx_state == DWC2_L2) {
hsotg->flags.b.port_suspend_change = 1;
else
usb_hcd_resume_root_hub(hsotg->priv);
} else {
hsotg->flags.b.port_l1_change = 1;
}
dwc2_root_intr(hsotg->hsotg_sc);
}
@ -361,12 +372,11 @@ void dwc2_hcd_stop(struct dwc2_hsotg *hsotg)
DWC2_WRITE_4(hsotg, HPRT0, 0);
}
int
dwc2_hcd_urb_enqueue(struct dwc2_hsotg *hsotg, struct dwc2_hcd_urb *urb,
void **ep_handle, gfp_t mem_flags)
/* Caller must hold driver lock */
int dwc2_hcd_urb_enqueue(struct dwc2_hsotg *hsotg,
struct dwc2_hcd_urb *urb, struct dwc2_qh *qh,
struct dwc2_qtd *qtd)
{
struct dwc2_softc *sc = hsotg->hsotg_sc;
struct dwc2_qtd *qtd;
u32 intr_mask;
int retval;
int dev_speed;
@ -379,32 +389,28 @@ dwc2_hcd_urb_enqueue(struct dwc2_hsotg *hsotg, struct dwc2_hcd_urb *urb,
dev_speed = dwc2_host_get_speed(hsotg, urb->priv);
/* Some core configurations cannot support LS traffic on a FS root port */
/* Some configurations cannot support LS traffic on a FS root port */
if ((dev_speed == USB_SPEED_LOW) &&
(hsotg->hw_params.fs_phy_type == GHWCFG2_FS_PHY_TYPE_DEDICATED) &&
(hsotg->hw_params.hs_phy_type == GHWCFG2_HS_PHY_TYPE_UTMI)) {
u32 hprt0 = DWC2_READ_4(hsotg, HPRT0);
u32 prtspd = (hprt0 & HPRT0_SPD_MASK) >> HPRT0_SPD_SHIFT;
if (prtspd == HPRT0_SPD_FULL_SPEED) {
if (prtspd == HPRT0_SPD_FULL_SPEED)
return -ENODEV;
}
}
qtd = pool_cache_get(sc->sc_qtdpool, PR_NOWAIT);
if (!qtd)
return -ENOMEM;
return -EINVAL;
memset(qtd, 0, sizeof(*qtd));
dwc2_hcd_qtd_init(qtd, urb);
retval = dwc2_hcd_qtd_add(hsotg, qtd, (struct dwc2_qh **)ep_handle,
mem_flags);
retval = dwc2_hcd_qtd_add(hsotg, qtd, qh);
if (retval) {
dev_err(hsotg->dev,
"DWC OTG HCD URB Enqueue failed adding QTD. Error status %d\n",
retval);
pool_cache_put(sc->sc_qtdpool, qtd);
return retval;
}
@ -665,18 +671,20 @@ static void *dwc2_hc_init_xfer(struct dwc2_hsotg *hsotg,
}
static int dwc2_hc_setup_align_buf(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
struct dwc2_host_chan *chan, void *bufptr)
struct dwc2_host_chan *chan,
struct dwc2_hcd_urb *urb, void *bufptr)
{
u32 buf_size;
if (chan->ep_type != USB_ENDPOINT_XFER_ISOC)
buf_size = hsotg->core_params->max_transfer_size;
else
buf_size = 4096;
if (!qh->dw_align_buf) {
int err;
if (chan->ep_type != USB_ENDPOINT_XFER_ISOC)
buf_size = hsotg->core_params->max_transfer_size;
else
/* 3072 = 3 max-size Isoc packets */
buf_size = 3072;
qh->dw_align_buf = NULL;
qh->dw_align_buf_dma = 0;
err = usb_allocmem(&hsotg->hsotg_sc->sc_bus, buf_size, buf_size,
@ -689,16 +697,26 @@ static int dwc2_hc_setup_align_buf(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
}
if (!qh->dw_align_buf)
return -ENOMEM;
qh->dw_align_buf_size = buf_size;
}
if (!chan->ep_is_in && chan->xfer_len) {
usb_syncmem(chan->xfer_usbdma, 0, buf_size,
BUS_DMASYNC_POSTWRITE);
memcpy(qh->dw_align_buf, bufptr, chan->xfer_len);
usb_syncmem(chan->xfer_usbdma, 0, buf_size,
BUS_DMASYNC_PREWRITE);
if (chan->xfer_len) {
dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__);
void *usb_urb = urb->priv;
if (usb_urb) {
if (!chan->ep_is_in) {
memcpy(qh->dw_align_buf, bufptr,
chan->xfer_len);
}
} else {
dev_warn(hsotg->dev, "no URB in dwc2_urb\n");
}
}
usb_syncmem(&qh->dw_align_buf_usbdma, 0, qh->dw_align_buf_size,
chan->ep_is_in ? BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE);
chan->align_buf = qh->dw_align_buf_dma;
return 0;
}
@ -793,8 +811,8 @@ static int dwc2_assign_and_init_hc(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
/* Non DWORD-aligned buffer case */
if (bufptr) {
dev_vdbg(hsotg->dev, "Non-aligned buffer%p\n", bufptr);
if (dwc2_hc_setup_align_buf(hsotg, qh, chan, bufptr)) {
dev_vdbg(hsotg->dev, "Non-aligned buffer\n");
if (dwc2_hc_setup_align_buf(hsotg, qh, chan, urb, bufptr)) {
dev_err(hsotg->dev,
"%s: Failed to allocate memory to handle non-dword aligned buffer\n",
__func__);
@ -1338,6 +1356,8 @@ dwc2_conn_id_status_change(struct work *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);
} else {
/* A-Device connector (Host Mode) */
dev_dbg(hsotg->dev, "connId A\n");
@ -1485,7 +1505,7 @@ dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
hprt0 |= HPRT0_RES;
DWC2_WRITE_4(hsotg, HPRT0, hprt0);
hprt0 &= ~HPRT0_SUSP;
usleep_range(100000, 150000);
msleep(USB_RESUME_TIMEOUT);
hprt0 &= ~HPRT0_RES;
DWC2_WRITE_4(hsotg, HPRT0, hprt0);
@ -1566,9 +1586,10 @@ dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
dev_dbg(hsotg->dev, "GetHubDescriptor\n");
hub_desc = (usb_hub_descriptor_t *)buf;
hub_desc->bDescLength = 9;
hub_desc->bDescriptorType = 0x29;
hub_desc->bDescriptorType = USB_DT_HUB;
hub_desc->bNbrPorts = 1;
USETW(hub_desc->wHubCharacteristics, 0x08);
USETW(hub_desc->wHubCharacteristics, HUB_CHAR_COMMON_LPSM |
HUB_CHAR_INDV_PORT_OCPM);
hub_desc->bPwrOn2PwrGood = 1;
hub_desc->bHubContrCurrent = 0;
hub_desc->DeviceRemovable[0] = 0;
@ -1727,6 +1748,15 @@ dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
/* Not supported */
break;
case USB_PORT_FEAT_TEST:
hprt0 = dwc2_read_hprt0(hsotg);
dev_dbg(hsotg->dev,
"SetPortFeature - USB_PORT_FEAT_TEST\n");
hprt0 &= ~HPRT0_TSTCTL_MASK;
hprt0 |= (windex >> 8) << HPRT0_TSTCTL_SHIFT;
DWC2_WRITE_4(hsotg, HPRT0, hprt0);
break;
default:
retval = -EINVAL;
dev_err(hsotg->dev,
@ -2067,7 +2097,6 @@ dwc2_hcd_reset_func(struct work *work)
* error code on failure.
*/
/*
* Frees secondary storage associated with the dwc2_hsotg structure contained
* in the struct usb_hcd field
@ -2141,42 +2170,23 @@ static void dwc2_hcd_release(struct dwc2_hsotg *hsotg)
dwc2_hcd_free(hsotg);
}
/*
* Sets all parameters to the given value.
*
* Assumes that the dwc2_core_params struct contains only integers.
*/
void dwc2_set_all_params(struct dwc2_core_params *params, int value)
{
int *p = (int *)params;
size_t size = sizeof(*params) / sizeof(*p);
int i;
for (i = 0; i < size; i++)
p[i] = value;
}
/*
* Initializes the HCD. This function allocates memory for and initializes the
* static parts of the usb_hcd and dwc2_hsotg structures. It also registers the
* USB bus with the core and calls the hc_driver->start() function. It returns
* a negative error on failure.
*/
int dwc2_hcd_init(struct dwc2_hsotg *hsotg,
const struct dwc2_core_params *params)
int dwc2_hcd_init(struct dwc2_hsotg *hsotg)
{
struct dwc2_host_chan *channel;
int i, num_channels;
int err, retval;
if (usb_disabled())
return -ENODEV;
dev_dbg(hsotg->dev, "DWC OTG HCD INIT\n");
/* Detect config values from hardware */
retval = dwc2_get_hwparams(hsotg);
if (retval)
return retval;
retval = -ENOMEM;
dev_dbg(hsotg->dev, "hcfg=%08x\n", DWC2_READ_4(hsotg, HCFG));
@ -2194,15 +2204,6 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg,
hsotg->last_frame_num = HFNUM_MAX_FRNUM;
#endif
hsotg->core_params = kmem_zalloc(sizeof(*hsotg->core_params), KM_SLEEP);
if (!hsotg->core_params)
goto error1;
dwc2_set_all_params(hsotg->core_params, -1);
/* Validate parameter values */
dwc2_set_parameters(hsotg, params);
spin_lock_init(&hsotg->lock);
/*
@ -2306,7 +2307,6 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg,
error3:
dwc2_hcd_release(hsotg);
error2:
error1:
kmem_free(hsotg->core_params, sizeof(*hsotg->core_params));
#ifdef CONFIG_USB_DWC2_TRACK_MISSED_SOFS
@ -2319,3 +2319,30 @@ error1:
dev_err(hsotg->dev, "%s() FAILED, returning %d\n", __func__, retval);
return retval;
}
/*
* Removes the HCD.
* Frees memory and resources associated with the HCD and deregisters the bus.
*/
void dwc2_hcd_remove(struct dwc2_hsotg *hsotg)
{
struct usb_hcd *hcd;
dev_dbg(hsotg->dev, "DWC OTG HCD REMOVE\n");
hcd = dwc2_hsotg_to_hcd(hsotg);
dev_dbg(hsotg->dev, "hsotg->hcd = %p\n", hcd);
if (!hcd) {
dev_dbg(hsotg->dev, "%s: dwc2_hsotg_to_hcd(hsotg) NULL!\n",
__func__);
return;
}
hsotg->priv = NULL;
dwc2_hcd_release(hsotg);
#ifdef CONFIG_USB_DWC2_TRACK_MISSED_SOFS
kfree(hsotg->last_frame_num_array);
kfree(hsotg->frame_num_array);
#endif
}

View File

@ -1,4 +1,4 @@
/* $NetBSD: dwc2_hcd.h,v 1.9 2014/09/03 10:00:08 skrll Exp $ */
/* $NetBSD: dwc2_hcd.h,v 1.10 2015/08/30 12:59:59 skrll Exp $ */
/*
* hcd.h - DesignWare HS OTG Controller host-mode declarations
@ -249,7 +249,8 @@ enum dwc2_transaction_type {
* @ntd: Actual number of transfer descriptors in a list
* @dw_align_buf: Used instead of original buffer if its physical address
* is not dword-aligned
* @dw_align_buf_dma: DMA address for align_buf
* @dw_align_buf_size: Size of dw_align_buf
* @dw_align_buf_dma: DMA address for dw_align_buf
* @qtd_list: List of QTDs for this QH
* @channel: Host channel currently processing transfers for this QH
* @qh_list_entry: Entry for QH in either the periodic or non-periodic
@ -284,6 +285,7 @@ struct dwc2_qh {
u16 ntd;
usb_dma_t dw_align_buf_usbdma;
u8 *dw_align_buf;
int dw_align_buf_size;
dma_addr_t dw_align_buf_dma;
struct list_head qtd_list;
struct dwc2_host_chan *channel;
@ -459,13 +461,8 @@ static inline u8 dwc2_hcd_is_pipe_out(struct dwc2_hcd_pipe_info *pipe)
return !dwc2_hcd_is_pipe_in(pipe);
}
extern int dwc2_hcd_init(struct dwc2_hsotg *hsotg,
const struct dwc2_core_params *params);
extern int dwc2_hcd_init(struct dwc2_hsotg *hsotg);
extern void dwc2_hcd_remove(struct dwc2_hsotg *hsotg);
extern void dwc2_set_parameters(struct dwc2_hsotg *hsotg,
const struct dwc2_core_params *params);
extern void dwc2_set_all_params(struct dwc2_core_params *params, int value);
extern int dwc2_get_hwparams(struct dwc2_hsotg *hsotg);
/* Transaction Execution Functions */
extern enum dwc2_transaction_type dwc2_hcd_select_transactions(
@ -476,6 +473,9 @@ extern void dwc2_hcd_queue_transactions(struct dwc2_hsotg *hsotg,
/* Schedule Queue Functions */
/* Implemented in hcd_queue.c */
extern void dwc2_hcd_init_usecs(struct dwc2_hsotg *hsotg);
extern struct dwc2_qh *dwc2_hcd_qh_create(struct dwc2_hsotg *hsotg,
struct dwc2_hcd_urb *urb,
gfp_t mem_flags);
extern void dwc2_hcd_qh_free(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh);
extern int dwc2_hcd_qh_add(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh);
extern void dwc2_hcd_qh_unlink(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh);
@ -484,7 +484,7 @@ extern void dwc2_hcd_qh_deactivate(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
extern void dwc2_hcd_qtd_init(struct dwc2_qtd *qtd, struct dwc2_hcd_urb *urb);
extern int dwc2_hcd_qtd_add(struct dwc2_hsotg *hsotg, struct dwc2_qtd *qtd,
struct dwc2_qh **qh, int mem_flags);
struct dwc2_qh *qh);
/* Removes and frees a QTD */
extern void dwc2_hcd_qtd_unlink_and_free(struct dwc2_hsotg *hsotg,
@ -667,9 +667,6 @@ extern irqreturn_t dwc2_handle_hcd_intr(struct dwc2_hsotg *hsotg);
*/
extern void dwc2_hcd_stop(struct dwc2_hsotg *hsotg);
extern void dwc2_hcd_start(struct dwc2_hsotg *hsotg);
extern void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg);
/**
* dwc2_hcd_is_b_host() - Returns 1 if core currently is acting as B host,
* and 0 otherwise
@ -678,13 +675,6 @@ extern void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg);
*/
extern int dwc2_hcd_is_b_host(struct dwc2_hsotg *hsotg);
/**
* dwc2_hcd_get_frame_number() - Returns current frame number
*
* @hsotg: The DWC2 HCD
*/
extern int dwc2_hcd_get_frame_number(struct dwc2_hsotg *hsotg);
/**
* dwc2_hcd_dump_state() - Dumps hsotg state
*
@ -774,8 +764,9 @@ int dwc2_hcd_urb_dequeue(struct dwc2_hsotg *, struct dwc2_hcd_urb *);
void dwc2_hcd_reinit(struct dwc2_hsotg *);
int dwc2_hcd_hub_control(struct dwc2_hsotg *, u16, u16, u16, char *, u16);
struct dwc2_hsotg *dwc2_hcd_to_hsotg(struct usb_hcd *);
int dwc2_hcd_urb_enqueue(struct dwc2_hsotg *, struct dwc2_hcd_urb *, void **,
gfp_t);
int dwc2_hcd_urb_enqueue(struct dwc2_hsotg *hsotg,
struct dwc2_hcd_urb *urb, struct dwc2_qh *qh,
struct dwc2_qtd *qtd);
void dwc2_hcd_urb_set_pipeinfo(struct dwc2_hsotg *, struct dwc2_hcd_urb *,
u8 ,u8, u8, u8, u16);

View File

@ -1,4 +1,4 @@
/* $NetBSD: dwc2_hcdintr.c,v 1.11 2014/11/24 10:14:14 skrll Exp $ */
/* $NetBSD: dwc2_hcdintr.c,v 1.12 2015/08/30 12:59:59 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.11 2014/11/24 10:14:14 skrll Exp $");
__KERNEL_RCSID(0, "$NetBSD: dwc2_hcdintr.c,v 1.12 2015/08/30 12:59:59 skrll Exp $");
#include <sys/types.h>
#include <sys/pool.h>
@ -357,6 +357,9 @@ static void dwc2_port_intr(struct dwc2_hsotg *hsotg)
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;
@ -474,12 +477,17 @@ static int dwc2_update_urb_state(struct dwc2_hsotg *hsotg,
}
/* Non DWORD-aligned buffer case handling */
if (chan->align_buf && xfer_length && chan->ep_is_in) {
if (chan->align_buf && xfer_length) {
dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__);
usb_syncmem(urb->usbdma, 0, urb->length, BUS_DMASYNC_POSTREAD);
memcpy(urb->buf + urb->actual_length, chan->qh->dw_align_buf,
xfer_length);
usb_syncmem(urb->usbdma, 0, urb->length, BUS_DMASYNC_PREREAD);
usb_syncmem(urb->usbdma, 0, chan->qh->dw_align_buf_size,
chan->ep_is_in ?
BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE);
if (chan->ep_is_in)
memcpy(urb->buf + urb->actual_length,
chan->qh->dw_align_buf, xfer_length);
usb_syncmem(urb->usbdma, 0, chan->qh->dw_align_buf_size,
chan->ep_is_in ?
BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE);
}
dev_vdbg(hsotg->dev, "urb->actual_length=%d xfer_length=%d\n",
@ -564,17 +572,22 @@ static enum dwc2_halt_status dwc2_update_isoc_urb_state(
chan, chnum, qtd, halt_status, NULL);
/* Non DWORD-aligned buffer case handling */
if (chan->align_buf && frame_desc->actual_length &&
chan->ep_is_in) {
if (chan->align_buf && frame_desc->actual_length) {
dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n",
__func__);
usb_syncmem(urb->usbdma, 0, urb->length,
BUS_DMASYNC_POSTREAD);
memcpy(urb->buf + frame_desc->offset +
qtd->isoc_split_offset, chan->qh->dw_align_buf,
frame_desc->actual_length);
usb_syncmem(urb->usbdma, 0, urb->length,
BUS_DMASYNC_PREREAD);
usb_dma_t *ud = &chan->qh->dw_align_buf_usbdma;
usb_syncmem(ud, 0, chan->qh->dw_align_buf_size,
chan->ep_is_in ?
BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE);
if (chan->ep_is_in)
memcpy(urb->buf + frame_desc->offset +
qtd->isoc_split_offset,
chan->qh->dw_align_buf,
frame_desc->actual_length);
usb_syncmem(ud, 0, chan->qh->dw_align_buf_size,
chan->ep_is_in ?
BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE);
}
break;
case DWC2_HC_XFER_FRAME_OVERRUN:
@ -597,17 +610,22 @@ static enum dwc2_halt_status dwc2_update_isoc_urb_state(
chan, chnum, qtd, halt_status, NULL);
/* Non DWORD-aligned buffer case handling */
if (chan->align_buf && frame_desc->actual_length &&
chan->ep_is_in) {
if (chan->align_buf && frame_desc->actual_length) {
dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n",
__func__);
usb_syncmem(urb->usbdma, 0, urb->length,
BUS_DMASYNC_POSTREAD);
memcpy(urb->buf + frame_desc->offset +
qtd->isoc_split_offset, chan->qh->dw_align_buf,
frame_desc->actual_length);
usb_syncmem(urb->usbdma, 0, urb->length,
BUS_DMASYNC_PREREAD);
usb_dma_t *ud = &chan->qh->dw_align_buf_usbdma;
usb_syncmem(ud, 0, chan->qh->dw_align_buf_size,
chan->ep_is_in ?
BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE);
if (chan->ep_is_in)
memcpy(urb->buf + frame_desc->offset +
qtd->isoc_split_offset,
chan->qh->dw_align_buf,
frame_desc->actual_length);
usb_syncmem(ud, 0, chan->qh->dw_align_buf_size,
chan->ep_is_in ?
BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE);
}
/* Skip whole frame */
@ -943,12 +961,12 @@ static int dwc2_xfercomp_isoc_split_in(struct dwc2_hsotg *hsotg,
if (chan->align_buf) {
dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__);
usb_syncmem(qtd->urb->usbdma, 0, qtd->urb->length,
BUS_DMASYNC_POSTREAD);
usb_syncmem(qtd->urb->usbdma, chan->qh->dw_align_buf_dma,
chan->qh->dw_align_buf_size, BUS_DMASYNC_POSTREAD);
memcpy(qtd->urb->buf + frame_desc->offset +
qtd->isoc_split_offset, chan->qh->dw_align_buf, len);
usb_syncmem(qtd->urb->usbdma, 0, qtd->urb->length,
BUS_DMASYNC_PREREAD);
usb_syncmem(qtd->urb->usbdma, chan->qh->dw_align_buf_dma,
chan->qh->dw_align_buf_size, BUS_DMASYNC_PREREAD);
}
qtd->isoc_split_offset += len;
@ -1175,10 +1193,19 @@ static void dwc2_update_urb_state_abn(struct dwc2_hsotg *hsotg,
/* Non DWORD-aligned buffer case handling */
if (chan->align_buf && xfer_length && chan->ep_is_in) {
dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__);
usb_syncmem(urb->usbdma, 0, urb->length, BUS_DMASYNC_POSTREAD);
memcpy(urb->buf + urb->actual_length, chan->qh->dw_align_buf,
xfer_length);
usb_syncmem(urb->usbdma, 0, urb->length, BUS_DMASYNC_PREREAD);
usb_dma_t *ud = &chan->qh->dw_align_buf_usbdma;
usb_syncmem(ud, 0, chan->qh->dw_align_buf_size,
chan->ep_is_in ?
BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE);
if (chan->ep_is_in)
memcpy(urb->buf + urb->actual_length,
chan->qh->dw_align_buf,
xfer_length);
usb_syncmem(ud, 0, chan->qh->dw_align_buf_size,
chan->ep_is_in ?
BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE);
}
urb->actual_length += xfer_length;
@ -1206,6 +1233,16 @@ static void dwc2_hc_nak_intr(struct dwc2_hsotg *hsotg,
struct dwc2_host_chan *chan, int chnum,
struct dwc2_qtd *qtd)
{
if (!qtd) {
dev_dbg(hsotg->dev, "%s: qtd is NULL\n", __func__);
return;
}
if (!qtd->urb) {
dev_dbg(hsotg->dev, "%s: qtd->urb is NULL\n", __func__);
return;
}
if (dbg_hc(chan))
dev_vdbg(hsotg->dev, "--Host Channel %d Interrupt: NAK Received--\n",
chnum);
@ -1910,10 +1947,10 @@ static void dwc2_hc_chhltd_intr_dma(struct dwc2_hsotg *hsotg,
"NYET/NAK/ACK/other in non-error case, 0x%08x\n",
chan->hcint);
error:
/* use the 3-strikes rule */
/* Failthrough: use 3-strikes rule */
qtd->error_count++;
dwc2_update_urb_state_abn(hsotg, chan, chnum, qtd->urb,
qtd, DWC2_HC_XFER_XACT_ERR);
qtd, DWC2_HC_XFER_XACT_ERR);
dwc2_hcd_save_data_toggle(hsotg, chan, chnum, qtd);
dwc2_halt_channel(hsotg, chan, qtd, DWC2_HC_XFER_XACT_ERR);
}

View File

@ -1,4 +1,4 @@
/* $NetBSD: dwc2_hcdqueue.c,v 1.11 2014/09/03 10:00:08 skrll Exp $ */
/* $NetBSD: dwc2_hcdqueue.c,v 1.12 2015/08/30 12:59:59 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.11 2014/09/03 10:00:08 skrll Exp $");
__KERNEL_RCSID(0, "$NetBSD: dwc2_hcdqueue.c,v 1.12 2015/08/30 12:59:59 skrll Exp $");
#include <sys/types.h>
#include <sys/kmem.h>
@ -206,7 +206,7 @@ static void dwc2_qh_init(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
*
* Return: Pointer to the newly allocated QH, or NULL on error
*/
static struct dwc2_qh *dwc2_hcd_qh_create(struct dwc2_hsotg *hsotg,
struct dwc2_qh *dwc2_hcd_qh_create(struct dwc2_hsotg *hsotg,
struct dwc2_hcd_urb *urb,
gfp_t mem_flags)
{
@ -251,8 +251,8 @@ void dwc2_hcd_qh_free(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
if (hsotg->core_params->dma_desc_enable > 0) {
dwc2_hcd_qh_free_ddma(hsotg, qh);
} else if (qh->dw_align_buf) {
/* XXXNH */
usb_freemem(&hsotg->hsotg_sc->sc_bus, &qh->dw_align_buf_usbdma);
usb_freemem(&sc->sc_bus, &qh->dw_align_buf_usbdma);
qh->dw_align_buf_dma = (dma_addr_t)0;
}
pool_cache_put(sc->sc_qhpool, qh);
@ -609,6 +609,7 @@ int dwc2_hcd_qh_add(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
&hsotg->non_periodic_sched_inactive);
return 0;
}
status = dwc2_schedule_periodic(hsotg, qh);
if (status)
return status;
@ -646,6 +647,7 @@ void dwc2_hcd_qh_unlink(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
list_del_init(&qh->qh_list_entry);
return;
}
dwc2_deschedule_periodic(hsotg, qh);
hsotg->periodic_qh_count--;
if (!hsotg->periodic_qh_count) {
@ -780,63 +782,39 @@ void dwc2_hcd_qtd_init(struct dwc2_qtd *qtd, struct dwc2_hcd_urb *urb)
/**
* dwc2_hcd_qtd_add() - Adds a QTD to the QTD-list of a QH
* Caller must hold driver lock.
*
* @hsotg: The DWC HCD structure
* @qtd: The QTD to add
* @qh: Out parameter to return queue head
* @mem_flags: Flag to do atomic alloc if needed
* @hsotg: The DWC HCD structure
* @qtd: The QTD to add
* @qh: Queue head to add qtd to
*
* Return: 0 if successful, negative error code otherwise
*
* Finds the correct QH to place the QTD into. If it does not find a QH, it
* will create a new QH. If the QH to which the QTD is added is not currently
* scheduled, it is placed into the proper schedule based on its EP type.
*
* HCD lock must be held and interrupts must be disabled on entry
* If the QH to which the QTD is added is not currently scheduled, it is placed
* into the proper schedule based on its EP type.
*/
int dwc2_hcd_qtd_add(struct dwc2_hsotg *hsotg, struct dwc2_qtd *qtd,
struct dwc2_qh **qh, gfp_t mem_flags)
struct dwc2_qh *qh)
{
struct dwc2_hcd_urb *urb = qtd->urb;
int allocated = 0;
KASSERT(mutex_owned(hsotg->lock));
int retval;
/*
* Get the QH which holds the QTD-list to insert to. Create QH if it
* doesn't exist.
*/
if (*qh == NULL) {
*qh = dwc2_hcd_qh_create(hsotg, urb, mem_flags);
if (*qh == NULL)
return -ENOMEM;
allocated = 1;
if (unlikely(!qh)) {
dev_err(hsotg->dev, "%s: Invalid QH\n", __func__);
retval = -EINVAL;
goto fail;
}
retval = dwc2_hcd_qh_add(hsotg, *qh);
retval = dwc2_hcd_qh_add(hsotg, qh);
if (retval)
goto fail;
qtd->qh = *qh;
list_add_tail(&qtd->qtd_list_entry, &(*qh)->qtd_list);
qtd->qh = qh;
list_add_tail(&qtd->qtd_list_entry, &qh->qtd_list);
return 0;
fail:
if (allocated) {
struct dwc2_qtd *qtd2, *qtd2_tmp;
struct dwc2_qh *qh_tmp = *qh;
*qh = NULL;
dwc2_hcd_qh_unlink(hsotg, qh_tmp);
/* Free each QTD in the QH's QTD list */
list_for_each_entry_safe(qtd2, qtd2_tmp, &qh_tmp->qtd_list,
qtd_list_entry)
dwc2_hcd_qtd_unlink_and_free(hsotg, qtd2, qh_tmp);
dwc2_hcd_qh_free(hsotg, qh_tmp);
}
return retval;
}

View File

@ -1,4 +1,4 @@
/* $NetBSD: dwc2_hw.h,v 1.2 2013/09/25 06:19:22 skrll Exp $ */
/* $NetBSD: dwc2_hw.h,v 1.3 2015/08/30 12:59:59 skrll Exp $ */
/*
* hw.h - DesignWare HS OTG Controller hardware definitions
@ -111,6 +111,7 @@
#define GUSBCFG_FSINTF (1 << 5)
#define GUSBCFG_ULPI_UTMI_SEL (1 << 4)
#define GUSBCFG_PHYIF16 (1 << 3)
#define GUSBCFG_PHYIF8 (0 << 3)
#define GUSBCFG_TOUTCAL_MASK (0x7 << 0)
#define GUSBCFG_TOUTCAL_SHIFT 0
#define GUSBCFG_TOUTCAL_LIMIT 0x7
@ -295,6 +296,7 @@
#define GHWCFG4_NUM_IN_EPS_MASK (0xf << 26)
#define GHWCFG4_NUM_IN_EPS_SHIFT 26
#define GHWCFG4_DED_FIFO_EN (1 << 25)
#define GHWCFG4_DED_FIFO_SHIFT 25
#define GHWCFG4_SESSION_END_FILT_EN (1 << 24)
#define GHWCFG4_B_VALID_FILT_EN (1 << 23)
#define GHWCFG4_A_VALID_FILT_EN (1 << 22)
@ -405,6 +407,7 @@
#define FIFOSIZE_DEPTH_SHIFT 16
#define FIFOSIZE_STARTADDR_MASK (0xffff << 0)
#define FIFOSIZE_STARTADDR_SHIFT 0
#define FIFOSIZE_DEPTH_GET(_x) (((_x) >> 16) & 0xffff)
/* Device mode registers */
@ -521,11 +524,11 @@
#define DXEPCTL_STALL (1 << 21)
#define DXEPCTL_SNP (1 << 20)
#define DXEPCTL_EPTYPE_MASK (0x3 << 18)
#define DXEPCTL_EPTYPE_SHIFT 18
#define DXEPCTL_EPTYPE_CONTROL 0
#define DXEPCTL_EPTYPE_ISO 1
#define DXEPCTL_EPTYPE_BULK 2
#define DXEPCTL_EPTYPE_INTTERUPT 3
#define DXEPCTL_EPTYPE_CONTROL (0x0 << 18)
#define DXEPCTL_EPTYPE_ISO (0x1 << 18)
#define DXEPCTL_EPTYPE_BULK (0x2 << 18)
#define DXEPCTL_EPTYPE_INTERRUPT (0x3 << 18)
#define DXEPCTL_NAKSTS (1 << 17)
#define DXEPCTL_DPID (1 << 16)
#define DXEPCTL_EOFRNUM (1 << 16)
@ -541,6 +544,7 @@
#define DIEPINT(_a) HSOTG_REG(0x908 + ((_a) * 0x20))
#define DOEPINT(_a) HSOTG_REG(0xB08 + ((_a) * 0x20))
#define DXEPINT_SETUP_RCVD (1 << 15)
#define DXEPINT_INEPNAKEFF (1 << 6)
#define DXEPINT_BACK2BACKSETUP (1 << 6)
#define DXEPINT_INTKNEPMIS (1 << 5)